Browse Source

Sep 13 : [ADD] Initial Commit 'purchase_recurring_orders'

pull/254/merge
RisvanaCybro 2 years ago
parent
commit
8fa6b75f67
  1. 48
      purchase_recurring_orders/README.rst
  2. 23
      purchase_recurring_orders/__init__.py
  3. 48
      purchase_recurring_orders/__manifest__.py
  4. 43
      purchase_recurring_orders/data/ir_cron_data.xml
  5. 11
      purchase_recurring_orders/data/ir_sequence_data.xml
  6. 6
      purchase_recurring_orders/doc/RELEASE_NOTES.md
  7. 26
      purchase_recurring_orders/models/__init__.py
  8. 36
      purchase_recurring_orders/models/agreement_renewal_line.py
  9. 83
      purchase_recurring_orders/models/purchase_order.py
  10. 404
      purchase_recurring_orders/models/purchase_recurring_agreement.py
  11. 77
      purchase_recurring_orders/models/recurring_agreement_line.py
  12. 38
      purchase_recurring_orders/models/res_partner.py
  13. 5
      purchase_recurring_orders/security/ir.model.access.csv
  14. BIN
      purchase_recurring_orders/static/description/assets/icons/check.png
  15. BIN
      purchase_recurring_orders/static/description/assets/icons/chevron.png
  16. BIN
      purchase_recurring_orders/static/description/assets/icons/cogs.png
  17. BIN
      purchase_recurring_orders/static/description/assets/icons/consultation.png
  18. BIN
      purchase_recurring_orders/static/description/assets/icons/ecom-black.png
  19. BIN
      purchase_recurring_orders/static/description/assets/icons/education-black.png
  20. BIN
      purchase_recurring_orders/static/description/assets/icons/hotel-black.png
  21. BIN
      purchase_recurring_orders/static/description/assets/icons/license.png
  22. BIN
      purchase_recurring_orders/static/description/assets/icons/lifebuoy.png
  23. BIN
      purchase_recurring_orders/static/description/assets/icons/manufacturing-black.png
  24. BIN
      purchase_recurring_orders/static/description/assets/icons/pos-black.png
  25. BIN
      purchase_recurring_orders/static/description/assets/icons/puzzle.png
  26. BIN
      purchase_recurring_orders/static/description/assets/icons/restaurant-black.png
  27. BIN
      purchase_recurring_orders/static/description/assets/icons/service-black.png
  28. BIN
      purchase_recurring_orders/static/description/assets/icons/trading-black.png
  29. BIN
      purchase_recurring_orders/static/description/assets/icons/training.png
  30. BIN
      purchase_recurring_orders/static/description/assets/icons/update.png
  31. BIN
      purchase_recurring_orders/static/description/assets/icons/user.png
  32. BIN
      purchase_recurring_orders/static/description/assets/icons/wrench.png
  33. BIN
      purchase_recurring_orders/static/description/assets/misc/categories.png
  34. BIN
      purchase_recurring_orders/static/description/assets/misc/check-box.png
  35. BIN
      purchase_recurring_orders/static/description/assets/misc/compass.png
  36. BIN
      purchase_recurring_orders/static/description/assets/misc/corporate.png
  37. BIN
      purchase_recurring_orders/static/description/assets/misc/customer-support.png
  38. BIN
      purchase_recurring_orders/static/description/assets/misc/cybrosys-logo.png
  39. BIN
      purchase_recurring_orders/static/description/assets/misc/features.png
  40. BIN
      purchase_recurring_orders/static/description/assets/misc/logo.png
  41. BIN
      purchase_recurring_orders/static/description/assets/misc/pictures.png
  42. BIN
      purchase_recurring_orders/static/description/assets/misc/pie-chart.png
  43. BIN
      purchase_recurring_orders/static/description/assets/misc/right-arrow.png
  44. BIN
      purchase_recurring_orders/static/description/assets/misc/star.png
  45. BIN
      purchase_recurring_orders/static/description/assets/misc/support.png
  46. BIN
      purchase_recurring_orders/static/description/assets/misc/whatsapp.png
  47. BIN
      purchase_recurring_orders/static/description/assets/modules/automatic_subscription.png
  48. BIN
      purchase_recurring_orders/static/description/assets/modules/budget_image.png
  49. BIN
      purchase_recurring_orders/static/description/assets/modules/employee_image.png
  50. BIN
      purchase_recurring_orders/static/description/assets/modules/product_brand.png
  51. BIN
      purchase_recurring_orders/static/description/assets/modules/quotation_image.png
  52. BIN
      purchase_recurring_orders/static/description/assets/modules/section_subtotal.png
  53. BIN
      purchase_recurring_orders/static/description/assets/screenshots/1.png
  54. BIN
      purchase_recurring_orders/static/description/assets/screenshots/10.png
  55. BIN
      purchase_recurring_orders/static/description/assets/screenshots/11.png
  56. BIN
      purchase_recurring_orders/static/description/assets/screenshots/12.png
  57. BIN
      purchase_recurring_orders/static/description/assets/screenshots/13.png
  58. BIN
      purchase_recurring_orders/static/description/assets/screenshots/2.png
  59. BIN
      purchase_recurring_orders/static/description/assets/screenshots/3.png
  60. BIN
      purchase_recurring_orders/static/description/assets/screenshots/4.png
  61. BIN
      purchase_recurring_orders/static/description/assets/screenshots/5.png
  62. BIN
      purchase_recurring_orders/static/description/assets/screenshots/6.png
  63. BIN
      purchase_recurring_orders/static/description/assets/screenshots/7.png
  64. BIN
      purchase_recurring_orders/static/description/assets/screenshots/8.png
  65. BIN
      purchase_recurring_orders/static/description/assets/screenshots/9.png
  66. BIN
      purchase_recurring_orders/static/description/assets/screenshots/hero.gif
  67. BIN
      purchase_recurring_orders/static/description/banner.png
  68. BIN
      purchase_recurring_orders/static/description/icon.png
  69. 742
      purchase_recurring_orders/static/description/index.html
  70. 31
      purchase_recurring_orders/views/purchase_order_views.xml
  71. 172
      purchase_recurring_orders/views/purchase_recurring_agreement_views.xml
  72. 21
      purchase_recurring_orders/views/res_partner_views.xml
  73. 22
      purchase_recurring_orders/wizard/__init__.py
  74. 54
      purchase_recurring_orders/wizard/agreement_renewal.py
  75. 40
      purchase_recurring_orders/wizard/agreement_renewal_views.xml

48
purchase_recurring_orders/README.rst

@ -0,0 +1,48 @@
.. image:: https://img.shields.io/badge/licence-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/>`__
License
-------
Affero General Public License v3.0 (AGPL v3)
(https://www.gnu.org/licenses/agpl-3.0-standalone.html)
Credits
-------
Developer: (V15) Ruksana 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>`__

23
purchase_recurring_orders/__init__.py

@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Ruksana P (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

48
purchase_recurring_orders/__manifest__.py

@ -0,0 +1,48 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Ruksana P (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': '15.0.1.0.0',
'category': '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/agreement_renewal_views.xml',
'views/purchase_recurring_agreement_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,
}

43
purchase_recurring_orders/data/ir_cron_data.xml

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo noupdate="1">
<!-- Scheduled Action for Prolongation check of recurring orders
agreements-->
<record id="ir_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="ir_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="ir_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>
</odoo>

11
purchase_recurring_orders/data/ir_sequence_data.xml

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo noupdate="1">
<!-- Sequence Generator-->
<record model="ir.sequence" id="ir_sequence_recurring_agreement">
<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>
<field name="company_id" eval="False"/>
</record>
</odoo>

6
purchase_recurring_orders/doc/RELEASE_NOTES.md

@ -0,0 +1,6 @@
## Module <purchase_recurring_orders>
#### 14.08.2023
#### Version 15.0.1.0.0
#### ADD
- Initial commit for Purchase Recurring Orders

26
purchase_recurring_orders/models/__init__.py

@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Ruksana P (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 agreement_renewal_line
from . import purchase_order
from . import purchase_recurring_agreement
from . import recurring_agreement_line
from . import res_partner

36
purchase_recurring_orders/models/agreement_renewal_line.py

@ -0,0 +1,36 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Ruksana P (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 AgreementRenewalLine(models.Model):
""" Purchase recurring agreement renewal records of particular agreement"""
_name = 'agreement.renewal.line'
_description = "Purchase Agreement Renewal Records"
recurring_agreement_id = fields.Many2one('purchase.recurring.agreement',
string='Agreement Reference',
help='Recurring agreement for '
'renewal', ondelete='cascade')
date = fields.Datetime(string='Date', help="Date of the Renewal")
comments = fields.Char(string='Comments', size=200,
help='Add renewal comments')

83
purchase_recurring_orders/models/purchase_order.py

@ -0,0 +1,83 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Ruksana P (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 for adding two fields is_agreement and
recurring_agreement_id """
_inherit = 'purchase.order'
is_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',
ondelete='restrict',
help="This indicates the Purchase"
" Agreement")
@api.model
def _prepare_agreement_vals(self, order):
""" Method for creating agreement values"""
return {
'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_generate_agreement(self):
"""Generates Purchase Recurring Agreement"""
agreements = []
for purchase_order in self:
agreement_vals = self._prepare_agreement_vals(purchase_order)
agreement = self.env[
'purchase.recurring.agreement'].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)
self.env['recurring.agreement.line'].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

404
purchase_recurring_orders/models/purchase_recurring_agreement.py

@ -0,0 +1,404 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Ruksana P (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"
def _default_company_id(self):
"""Returns the Current Company id"""
company = self.env['res.company']._company_default_get('purchase')
return company
name = fields.Char(string='Sequence Number', index=True, size=32,
copy=False, help="Sequence number of agreement",
readonly=True, default=_('New'))
active = fields.Boolean(string='Active', default=True,
help='Unchecking this field, quotation for that '
'product is not generated')
partner_id = fields.Many2one('res.partner', string='Supplier', index=True,
change_default=True, required=True,
help="Supplier you are making the agreement")
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=" Starting 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([('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',
help='Agreement product records')
order_ids = fields.One2many('purchase.order', copy=False,
inverse_name='recurring_agreement_id',
string='Orders', readonly=True,
help='Purchase orders for this agreement')
renewal_ids = fields.One2many('agreement.renewal.line', copy=False,
string='Renewal Lines', readonly=True,
inverse_name='recurring_agreement_id',
help='Renewal records')
last_renovation_date = fields.Datetime(
string='Last Renovation Date',
onchange='_onchange_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", string='Next Expiration Date',
help="Date when agreement will expired")
state = fields.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([('not_renewed', 'Agreement not Renewed'),
('renewed', 'Agreement Renewed')],
string='Renewal State', readonly=True,
help="Renewal Status of the Recurring "
"agreement", 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",
string='Order Count')
_sql_constraints = [
('name_uniq', 'unique(name)', 'Agreement Number Must be Unique !'),
]
@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 _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', '=', record.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('name'):
vals['name'] = 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(x) is not None for x in
['active', 'name', '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_last_renovation_date(self):
"""Method for updating last renovation date"""
self.last_renovation_date = self.start_date
@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.name,
'partner_id': agreement.partner_id.id,
'state': 'draft', 'company_id': agreement.company_id.id,
'is_agreement': True,
'recurring_agreement_id': agreement.id,
'date_planned': date,
'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 = agreement_line_ids.product_id
product_lang = product.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': product.id,
'product_qty': agreement_line_ids.quantity,
'date_planned': order.date_planned,
'price_unit': product._get_tax_included_unit_price(
order.company_id, order.currency_id, order.date_order,
'purchase',
fiscal_position=order.fiscal_position_id,
product_uom=product.uom_po_id),
'product_uom': product.uom_po_id.id or product.uom_id.id,
'name': product_lang.display_name,
'taxes_id': fpos.map_tax(
product.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.is_active:
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])
@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_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()
def action_view_purchase_order(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 action_generate_next_year_orders(self):
"""This will Generate Orders for Next year"""
return self.generate_next_orders(years=1)
def action_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(
'is_active')
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
}

77
purchase_recurring_orders/models/recurring_agreement_line.py

@ -0,0 +1,77 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Ruksana P (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
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 Product List'
is_active = fields.Boolean(string='Active', default=True,
help='Unchecking this field, the quotation for'
' this product 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,
help='Choose product here')
uom_id = fields.Many2one(related='product_id.product_tmpl_id.uom_id',
help="UOM of the product", string="Uom")
additional_description = fields.Char(string='Description', size=30,
help='Additional description for the '
'product.')
quantity = fields.Float(string='Quantity', required=True,
help='Quantity of the product', default=1.0)
ordering_interval = fields.Integer(string='Interval', required=True,
help="Interval in time units for making "
"an order of this product",
default=1)
ordering_unit = fields.Selection(string='Interval Unit', required=True,
selection=[('days', 'Days'),
('weeks', 'Weeks'),
('months', 'Months'),
('years', 'Years')],
help="It indicated the recurring Time Unit",
default='months')
last_order_date = fields.Datetime(string='Last Order',
help='Date of the last Purchase order.')
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', readonly=True,
string="List Price",
help='Unit price of product')
_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'),
]

38
purchase_recurring_orders/models/res_partner.py

@ -0,0 +1,38 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Ruksana P (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 models
class ResPartner(models.Model):
""" Add new smart button agreements to view all purchase agreements"""
_inherit = 'res.partner'
def action_purchase_agreement(self):
"""Method of agreement smart button """
return {
'name': 'agreements',
'type': 'ir.actions.act_window',
'res_model': 'purchase.recurring.agreement',
'view_mode': 'tree,form',
'view_id': False,
'domain': [('partner_id', '=', self.id)]
}

5
purchase_recurring_orders/security/ir.model.access.csv

@ -0,0 +1,5 @@
id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink
access_purchase_recurring_agreement,purchase.recurring.agreement,model_purchase_recurring_agreement,purchase.group_purchase_user,1,1,1,1
access_recurring_agreement_line,recurring.agreement.line,model_recurring_agreement_line,purchase.group_purchase_user,1,1,1,1
access_agreement_renewal_line,agreement.renewal.line,model_agreement_renewal_line,purchase.group_purchase_user,1,1,1,1
access_agreement_renewal,agreement.renewal,model_agreement_renewal,purchase.group_purchase_user,1,1,1,1
1 id name model_id/id group_id/id perm_read perm_write perm_create perm_unlink
2 access_purchase_recurring_agreement purchase.recurring.agreement model_purchase_recurring_agreement purchase.group_purchase_user 1 1 1 1
3 access_recurring_agreement_line recurring.agreement.line model_recurring_agreement_line purchase.group_purchase_user 1 1 1 1
4 access_agreement_renewal_line agreement.renewal.line model_agreement_renewal_line purchase.group_purchase_user 1 1 1 1
5 access_agreement_renewal agreement.renewal model_agreement_renewal purchase.group_purchase_user 1 1 1 1

BIN
purchase_recurring_orders/static/description/assets/icons/check.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

BIN
purchase_recurring_orders/static/description/assets/icons/chevron.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 310 B

BIN
purchase_recurring_orders/static/description/assets/icons/cogs.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
purchase_recurring_orders/static/description/assets/icons/consultation.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
purchase_recurring_orders/static/description/assets/icons/ecom-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 576 B

BIN
purchase_recurring_orders/static/description/assets/icons/education-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 733 B

BIN
purchase_recurring_orders/static/description/assets/icons/hotel-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 911 B

BIN
purchase_recurring_orders/static/description/assets/icons/license.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
purchase_recurring_orders/static/description/assets/icons/lifebuoy.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
purchase_recurring_orders/static/description/assets/icons/manufacturing-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 673 B

BIN
purchase_recurring_orders/static/description/assets/icons/pos-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 878 B

BIN
purchase_recurring_orders/static/description/assets/icons/puzzle.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 653 B

BIN
purchase_recurring_orders/static/description/assets/icons/restaurant-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 905 B

BIN
purchase_recurring_orders/static/description/assets/icons/service-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 839 B

BIN
purchase_recurring_orders/static/description/assets/icons/trading-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 427 B

BIN
purchase_recurring_orders/static/description/assets/icons/training.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 627 B

BIN
purchase_recurring_orders/static/description/assets/icons/update.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
purchase_recurring_orders/static/description/assets/icons/user.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 988 B

BIN
purchase_recurring_orders/static/description/assets/icons/wrench.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
purchase_recurring_orders/static/description/assets/misc/categories.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
purchase_recurring_orders/static/description/assets/misc/check-box.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
purchase_recurring_orders/static/description/assets/misc/compass.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
purchase_recurring_orders/static/description/assets/misc/corporate.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
purchase_recurring_orders/static/description/assets/misc/customer-support.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
purchase_recurring_orders/static/description/assets/misc/cybrosys-logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

BIN
purchase_recurring_orders/static/description/assets/misc/features.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 589 B

BIN
purchase_recurring_orders/static/description/assets/misc/logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
purchase_recurring_orders/static/description/assets/misc/pictures.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
purchase_recurring_orders/static/description/assets/misc/pie-chart.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
purchase_recurring_orders/static/description/assets/misc/right-arrow.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 967 B

BIN
purchase_recurring_orders/static/description/assets/misc/star.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
purchase_recurring_orders/static/description/assets/misc/support.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
purchase_recurring_orders/static/description/assets/misc/whatsapp.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

BIN
purchase_recurring_orders/static/description/assets/modules/automatic_subscription.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

BIN
purchase_recurring_orders/static/description/assets/modules/budget_image.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

BIN
purchase_recurring_orders/static/description/assets/modules/employee_image.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

BIN
purchase_recurring_orders/static/description/assets/modules/product_brand.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

BIN
purchase_recurring_orders/static/description/assets/modules/quotation_image.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

BIN
purchase_recurring_orders/static/description/assets/modules/section_subtotal.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

BIN
purchase_recurring_orders/static/description/assets/screenshots/1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 KiB

BIN
purchase_recurring_orders/static/description/assets/screenshots/10.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

BIN
purchase_recurring_orders/static/description/assets/screenshots/11.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

BIN
purchase_recurring_orders/static/description/assets/screenshots/12.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 KiB

BIN
purchase_recurring_orders/static/description/assets/screenshots/13.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 KiB

BIN
purchase_recurring_orders/static/description/assets/screenshots/2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

BIN
purchase_recurring_orders/static/description/assets/screenshots/3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

BIN
purchase_recurring_orders/static/description/assets/screenshots/4.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

BIN
purchase_recurring_orders/static/description/assets/screenshots/5.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

BIN
purchase_recurring_orders/static/description/assets/screenshots/6.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

BIN
purchase_recurring_orders/static/description/assets/screenshots/7.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

BIN
purchase_recurring_orders/static/description/assets/screenshots/8.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 224 KiB

BIN
purchase_recurring_orders/static/description/assets/screenshots/9.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

BIN
purchase_recurring_orders/static/description/assets/screenshots/hero.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 437 KiB

BIN
purchase_recurring_orders/static/description/banner.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

BIN
purchase_recurring_orders/static/description/icon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

742
purchase_recurring_orders/static/description/index.html

@ -0,0 +1,742 @@
<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: #714B67; 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
style="color: #017E84; 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>Odoo.sh
</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;">
Purchase Recurring Orders</h1>
<p style="color:#FFFFFF; padding: 8px 15px; text-align: center; font-size: 24px;">
Helps To Create Recurring Purchase Orders Based On The
Agreements Provided</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 is useful to create recurring orders for
purchases based
on information(prolongation, Interval, etc.,) provided.
</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: 40px; margin-bottom: 40px">
<img src="assets/misc/check-box.png" class="mr-2"/>
<span style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Creates recurring orders for purchase</span>
</div>
<div class="d-flex align-items-center"
style="margin-top: 40px; margin-bottom: 40px">
<img src="assets/misc/check-box.png" class="mr-2"/>
<span style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Scheduled action for creating recurring orders</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;">
Menu for viewing and creating Recurring Order
Agreements</h3>
<img src="assets/screenshots/1.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 Recurring Order Agreement</h3>
<p>Create new recurring order agreement by filling the
details in
the form and add the products in Agreement Order
Line with
agreed price.</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;">
Generate Initial Order</h3>
<p>Click on the GENERATE INITIAL ORDER button for
generating the
first order.</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;">
Initial order is generated</h3>
<p>
First purchase order generated according to the
details added in
the recurring purchase agreement.
</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;">
Smart button for Orders</h3>
<p>
We can see all orders created from the recurring
purchase
agreement from the smart button which shows the
number of
orders.
</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;">
Generate Next year Orders</h3>
<p>
It is possible to create next year orders at one
click.
</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;">
Number of orders are increased
</h3>
<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;">
Generated Orders</h3>
<p>
All orders generated including initial and next year
orders.
</p>
<img src="assets/screenshots/8.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;">
Renew agreement</h3>
<img src="assets/screenshots/9.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;">
Update the comments and renew the Agreement</h3>
<img src="assets/screenshots/10.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;">
Scheduled action for generating recurring orders for
next
year</h3>
<img src="assets/screenshots/11.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;">
Generate Agreement button in purchase order</h3>
<img src="assets/screenshots/12.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 new agreement from current purchase
order</h3>
<img src="assets/screenshots/13.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-lg-12 d-flex flex-column justify-content-center"
style="text-align: center; padding: 2.5rem 1rem !important;">
<h2 style="color: #212529 !important;">Suggested
Products</h2>
<hr
style="border: 3px solid #714B67 !important; background-color: #714B67 !important; width: 80px !important; margin-bottom: 2rem !important;"/>
<div id="demo1" class="row carousel slide"
data-ride="carousel">
<!-- The slideshow -->
<div class="carousel-inner">
<div class="carousel-item active"
style="min-height:0px">
<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/product_brand_purchase/"
target="_blank">
<div style="border-radius:10px">
<img class="img img-responsive center-block"
style="border-top-left-radius:10px; border-top-right-radius:10px"
src="./assets/modules/product_brand.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/no_automatic_subscription/"
target="_blank">
<div style="border-radius:10px">
<img class="img img-responsive center-block"
style="border-top-left-radius:10px; border-top-right-radius:10px"
src="./assets/modules/automatic_subscription.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/section_wise_subtotal/"
target="_blank">
<div style="border-radius:10px">
<img class="img img-responsive center-block"
style="border-top-left-radius:10px; border-top-right-radius:10px"
src="./assets/modules/section_subtotal.png">
</div>
</a>
</div>
</div>
<div class="carousel-item" style="min-height:0px">
<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/base_account_budget/"
target="_blank">
<div style="border-radius:10px">
<img class="img img-responsive center-block"
style="border-top-left-radius:10px; border-top-right-radius:10px"
src="./assets/modules/budget_image.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/product_to_quotation/"
target="_blank">
<div style="border-radius:10px">
<img class="img img-responsive center-block"
style="border-top-left-radius:10px; border-top-right-radius:10px"
src="./assets/modules/quotation_image.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/employee_documents_expiry/"
target="_blank">
<div style="border-radius:10px">
<img class="img img-responsive center-block"
style="border-top-left-radius:10px; border-top-right-radius:10px"
src="./assets/modules/employee_image.png">
</div>
</a>
</div>
</div>
</div>
<!-- Left and right controls -->
<a class="carousel-control-prev" href="#demo1"
data-slide="prev"
style="left:-25px;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="right:-25px;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 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 &amp; 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 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"
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"
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 -->

31
purchase_recurring_orders/views/purchase_order_views.xml

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!--Generate Agreement Button in Purchase Order-->
<record id="purchase_order_form" model="ir.ui.view">
<field name="name">
purchase.order.view.form.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_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.view.list.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="is_agreement"
domain="[('is_agreement','=',False)]"/>
<separator/>
</field>
</field>
</record>
</odoo>

172
purchase_recurring_orders/views/purchase_recurring_agreement_views.xml

@ -0,0 +1,172 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Purchase Recurring Agreement tree View-->
<record id="purchase_recurring_agreement_view_tree" model="ir.ui.view">
<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="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="action_generate_initial_order"
class="oe_highlight"
states="empty"
string="Generate Initial Order"
type="object"/>
<button name="action_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"
attrs="{'invisible': [('prolong','!=', 'recurrent')]}"/>
</header>
<sheet>
<div name="button_box" class="oe_button_box">
<button class="oe_stat_button" type="object"
name="action_view_purchase_order"
icon="fa-file"
attrs="{'invisible' : [('order_count','=',0)]}">
<field string="Orders" name="order_count"
widget="statinfo"/>
</button>
</div>
<div class="oe_title">
<h1>
<field name="name"/>
</h1>
</div>
<group>
<group>
<field name="partner_id"/>
<field name="company_id"
groups="base.group_multi_company"/>
<field name="prolong"
attrs="{'readonly':[('renewal_state','!=','not_renewed')]}"/>
<field name="last_renovation_date" readonly="1"
attrs="{'invisible': [('prolong','!=', 'recurrent')]}"/>
<field name="state" invisible="1"/>
<field name="renewal_state" invisible="1"/>
</group>
<group>
<field name="active" invisible="1"/>
<field name="name" invisible="1"/>
<field name="start_date"
attrs="{'readonly':[('state','!=','empty')]}"/>
<field name="next_expiration_date"/>
<field name="prolong_unit"
attrs="{'required': [('prolong', '!=', 'fixed')]}"/>
</group>
<group>
<group>
<group attrs="{'invisible': [('prolong','=', 'fixed')]}"
colspan="4" col="4">
<field name="prolong_interval"
attrs="{'required': [('prolong', '!=', 'fixed')]}"/>
</group>
<group attrs="{'invisible': [('prolong','!=', 'fixed')]}"
colspan="4" col="2">
<field name="end_date"
attrs="{'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="is_active" optional="hide"/>
<field name="product_id"/>
<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"
attrs="{'invisible': [('prolong','!=', 'recurrent')]}">
<field colspan="4" mode="tree" name="renewal_ids"
widget="one2many_list" nolabel="1">
<tree string="Agreement Renewals">
<field name="date"/>
<field name="comments"/>
</tree>
</field>
</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="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 id="purchases_recurring_agreement_action"
model="ir.actions.act_window">
<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="purchases_recurring_agreement_menu_agreement"
parent="purchase.menu_procurement_management"
action="purchases_recurring_agreement_action"
sequence="4"/>
</odoo>

21
purchase_recurring_orders/views/res_partner_views.xml

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Agreement Smart Button-->
<record id="view_partner_form" model="ir.ui.view">
<field name="name">
res.partner.view.form.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="action_purchase_agreement"
type="object"
class="oe_stat_button"
icon="fa-file-text-o"
string="Agreements"/>
</xpath>
</field>
</record>
</odoo>

22
purchase_recurring_orders/wizard/__init__.py

@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Ruksana P (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 agreement_renewal

54
purchase_recurring_orders/wizard/agreement_renewal.py

@ -0,0 +1,54 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Ruksana P (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 AgreementRenewal(models.TransientModel):
""" New wizard for agreement renewal"""
_name = "agreement.renewal"
_description = "Purchase Agreement Renewal"
def _default_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=_default_date)
comments = fields.Char(string='Comments', size=200, help='Renewal comments')
def action_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['agreement.renewal.line'].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

40
purchase_recurring_orders/wizard/agreement_renewal_views.xml

@ -0,0 +1,40 @@
<?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="name">agreement.renewal.view.form</field>
<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>
</sheet>
<footer>
<button special="cancel" class="oe_highlight"
string="Cancel" icon="fa-close"/>
<button name="action_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">form</field>
<field name="view_id"
ref="agreement_renewal_view_form"/>
<field name="target">new</field>
</record>
</odoo>
Loading…
Cancel
Save