Browse Source

May 23 : [ADD] Initial Commit 'purchase_recurring_orders'

pull/257/head
Sayooj 2 years ago
parent
commit
d60e545b31
  1. 41
      purchase_recurring_orders/README.rst
  2. 23
      purchase_recurring_orders/__init__.py
  3. 47
      purchase_recurring_orders/__manifest__.py
  4. 12
      purchase_recurring_orders/data/ir_cron_data.xml
  5. 45
      purchase_recurring_orders/data/ir_sequence_data.xml
  6. 7
      purchase_recurring_orders/doc/RELEASE_NOTES.md
  7. 25
      purchase_recurring_orders/models/__init__.py
  8. 34
      purchase_recurring_orders/models/purchase_agreement_renewal.py
  9. 97
      purchase_recurring_orders/models/purchase_order.py
  10. 421
      purchase_recurring_orders/models/purchase_recurring_agreement.py
  11. 90
      purchase_recurring_orders/models/recurring_agreement_line.py
  12. 5
      purchase_recurring_orders/security/ir.model.access.csv
  13. BIN
      purchase_recurring_orders/static/description/assets/icons/check.png
  14. BIN
      purchase_recurring_orders/static/description/assets/icons/chevron.png
  15. BIN
      purchase_recurring_orders/static/description/assets/icons/cogs.png
  16. BIN
      purchase_recurring_orders/static/description/assets/icons/consultation.png
  17. BIN
      purchase_recurring_orders/static/description/assets/icons/ecom-black.png
  18. BIN
      purchase_recurring_orders/static/description/assets/icons/education-black.png
  19. BIN
      purchase_recurring_orders/static/description/assets/icons/hotel-black.png
  20. BIN
      purchase_recurring_orders/static/description/assets/icons/license.png
  21. BIN
      purchase_recurring_orders/static/description/assets/icons/lifebuoy.png
  22. BIN
      purchase_recurring_orders/static/description/assets/icons/manufacturing-black.png
  23. BIN
      purchase_recurring_orders/static/description/assets/icons/pos-black.png
  24. BIN
      purchase_recurring_orders/static/description/assets/icons/puzzle.png
  25. BIN
      purchase_recurring_orders/static/description/assets/icons/restaurant-black.png
  26. BIN
      purchase_recurring_orders/static/description/assets/icons/service-black.png
  27. BIN
      purchase_recurring_orders/static/description/assets/icons/trading-black.png
  28. BIN
      purchase_recurring_orders/static/description/assets/icons/training.png
  29. BIN
      purchase_recurring_orders/static/description/assets/icons/update.png
  30. BIN
      purchase_recurring_orders/static/description/assets/icons/user.png
  31. BIN
      purchase_recurring_orders/static/description/assets/icons/wrench.png
  32. BIN
      purchase_recurring_orders/static/description/assets/misc/categories.png
  33. BIN
      purchase_recurring_orders/static/description/assets/misc/check-box.png
  34. BIN
      purchase_recurring_orders/static/description/assets/misc/compass.png
  35. BIN
      purchase_recurring_orders/static/description/assets/misc/corporate.png
  36. BIN
      purchase_recurring_orders/static/description/assets/misc/customer-support.png
  37. BIN
      purchase_recurring_orders/static/description/assets/misc/cybrosys-logo.png
  38. BIN
      purchase_recurring_orders/static/description/assets/misc/features.png
  39. BIN
      purchase_recurring_orders/static/description/assets/misc/logo.png
  40. BIN
      purchase_recurring_orders/static/description/assets/misc/pictures.png
  41. BIN
      purchase_recurring_orders/static/description/assets/misc/pie-chart.png
  42. BIN
      purchase_recurring_orders/static/description/assets/misc/right-arrow.png
  43. BIN
      purchase_recurring_orders/static/description/assets/misc/star.png
  44. BIN
      purchase_recurring_orders/static/description/assets/misc/support.png
  45. BIN
      purchase_recurring_orders/static/description/assets/misc/whatsapp.png
  46. BIN
      purchase_recurring_orders/static/description/assets/modules/l1.png
  47. BIN
      purchase_recurring_orders/static/description/assets/modules/l2.png
  48. BIN
      purchase_recurring_orders/static/description/assets/modules/l3.png
  49. BIN
      purchase_recurring_orders/static/description/assets/modules/l4.png
  50. BIN
      purchase_recurring_orders/static/description/assets/modules/l5.png
  51. BIN
      purchase_recurring_orders/static/description/assets/modules/l6.png
  52. BIN
      purchase_recurring_orders/static/description/assets/screenshots/Screenshot1.png
  53. BIN
      purchase_recurring_orders/static/description/assets/screenshots/Screenshot10.png
  54. BIN
      purchase_recurring_orders/static/description/assets/screenshots/Screenshot11.png
  55. BIN
      purchase_recurring_orders/static/description/assets/screenshots/Screenshot2.png
  56. BIN
      purchase_recurring_orders/static/description/assets/screenshots/Screenshot3.png
  57. BIN
      purchase_recurring_orders/static/description/assets/screenshots/Screenshot4.png
  58. BIN
      purchase_recurring_orders/static/description/assets/screenshots/Screenshot5.png
  59. BIN
      purchase_recurring_orders/static/description/assets/screenshots/Screenshot6.png
  60. BIN
      purchase_recurring_orders/static/description/assets/screenshots/Screenshot7.png
  61. BIN
      purchase_recurring_orders/static/description/assets/screenshots/Screenshot8.png
  62. BIN
      purchase_recurring_orders/static/description/assets/screenshots/Screenshot9.png
  63. BIN
      purchase_recurring_orders/static/description/assets/screenshots/hero.gif
  64. BIN
      purchase_recurring_orders/static/description/banner.png
  65. BIN
      purchase_recurring_orders/static/description/icon.png
  66. 677
      purchase_recurring_orders/static/description/index.html
  67. 27
      purchase_recurring_orders/views/purchase_order_view.xml
  68. 183
      purchase_recurring_orders/views/recurring_orders_view.xml
  69. 27
      purchase_recurring_orders/views/res_partner_view.xml
  70. 22
      purchase_recurring_orders/wizard/__init__.py
  71. 56
      purchase_recurring_orders/wizard/renew_wizard.py
  72. 46
      purchase_recurring_orders/wizard/renew_wizard_view.xml

41
purchase_recurring_orders/README.rst

@ -0,0 +1,41 @@
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
:target: http://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: Unnimaya C O @cybrosys, 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: Unnimaya C O (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

47
purchase_recurring_orders/__manifest__.py

@ -0,0 +1,47 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Unnimaya C O (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': '16.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',
'images': ['static/description/banner.png'],
'website': 'https://www.cybrosys.com',
'depends': ['base', 'purchase'],
'data': [
'security/ir.model.access.csv',
'data/ir_cron_data.xml',
'data/ir_sequence_data.xml',
'wizard/renew_wizard_view.xml',
'views/recurring_orders_view.xml',
'views/purchase_order_view.xml',
'views/res_partner_view.xml',
],
'license': 'AGPL-3',
'installable': True,
'auto_install': False,
'application': False,
}

12
purchase_recurring_orders/data/ir_cron_data.xml

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<!-- Sequence Generator-->
<record model="ir.sequence" id="seq_ro_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>
</record>
</data>
</odoo>

45
purchase_recurring_orders/data/ir_sequence_data.xml

@ -0,0 +1,45 @@
<?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>

7
purchase_recurring_orders/doc/RELEASE_NOTES.md

@ -0,0 +1,7 @@
## Module <purchase_recurring_orders>
#### 05.03.2023
#### Version 16.0.1.0.0
#### ADD
- Initial commit for Purchase Recurring Orders

25
purchase_recurring_orders/models/__init__.py

@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Unnimaya C O (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_order
from . import purchase_recurring_agreement
from . import purchase_agreement_renewal
from . import recurring_agreement_line

34
purchase_recurring_orders/models/purchase_agreement_renewal.py

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

97
purchase_recurring_orders/models/purchase_order.py

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

421
purchase_recurring_orders/models/purchase_recurring_agreement.py

@ -0,0 +1,421 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Unnimaya C O (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(x) is not None for x 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()

90
purchase_recurring_orders/models/recurring_agreement_line.py

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

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_purchase_agreement_renewal,purchase.agreement.renewal,model_purchase_agreement_renewal,purchase.group_purchase_user,1,1,1,1
access_agreement_renewal_wizard,agreement.renewal.wizard,model_agreement_renewal_wizard,purchase.group_purchase_user,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_purchase_agreement_renewal purchase.agreement.renewal model_purchase_agreement_renewal purchase.group_purchase_user 1 1 1 1
5 access_agreement_renewal_wizard agreement.renewal.wizard model_agreement_renewal_wizard purchase.group_purchase_user 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/l1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 KiB

BIN
purchase_recurring_orders/static/description/banner.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

BIN
purchase_recurring_orders/static/description/icon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

677
purchase_recurring_orders/static/description/index.html

@ -0,0 +1,677 @@
<div style="background-color: #714B67; min-height: 600px; width: 100%; padding: 15px; position: relative;">
<!-- TITLE BAR -->
<div
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: #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>Enterprise
</div>
</div>
</div>
<!-- END OF TITLE BAR -->
<!-- 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 orders for Purchases based on the information provided</p>
<!-- END OF APP HERO -->
<img src="assets/screenshots/hero.gif"
style="width: 75%; height: auto; position: absolute; margin-left: auto; margin-right: auto; top: 45%; left: 12%; right: auto;"/>
</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/Screenshot1.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.</p>
<img src="assets/screenshots/Screenshot2.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/Screenshot3.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 generated</h3>
<p>
First Purchase Order Generated according to the details added in the Recurring purchase agreement.
</p>
<img src="assets/screenshots/Screenshot4.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/Screenshot5.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/Screenshot6.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 increased
</h3>
<img src="assets/screenshots/Screenshot7.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/Screenshot8.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/Screenshot9.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/Screenshot10.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/Screenshot11.png" class="img-thumbnail">
</div>
</div>
</div>
<!-- END OF SCREENSHOTS SECTION -->
<!-- RELATED PRODUCTS -->
<div class="d-flex align-items-center"
style="border-bottom: 2px solid #714B67; padding: 15px 0px;">
<div class="d-flex justify-content-center align-items-center mr-2"
style="background-color: #F5F5F5; border-radius: 0px; width: 40px; height: 40px;">
<img src="assets/misc/categories.png"/>
</div>
<h2 class="mt-2"
style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;">
Related
Products
</h2>
</div>
<div class="row">
<div class="col-sm-12">
<div id="demo1" class="row carousel slide" data-ride="carousel">
<!-- The slideshow -->
<div class="carousel-inner" style="padding: 30px;">
<div class="carousel-item" style="min-height: 198.656px;">
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16"
style="float:left">
<a href="https://apps.odoo.com/apps/modules/16.0/website_repeat_sale/"
target="_blank">
<div style="border-radius:10px">
<img class="img img-responsive center-block"
style="border-radius: 0px;"
src="assets/modules/l1.png">
</div>
</a>
</div>
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16"
style="float:left">
<a href="https://apps.odoo.com/apps/modules/16.0/woo_commerce/"
target="_blank">
<div style="border-radius:10px">
<img class="img img-responsive center-block"
style="border-radius: 0px;"
src="assets/modules/l2.png">
</div>
</a>
</div>
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16"
style="float:left">
<a href="https://apps.odoo.com/apps/modules/16.0/shopify_odoo_connector/"
target="_blank">
<div style="border-radius:10px">
<img class="img img-responsive center-block"
style="border-radius: 0px;"
src="assets/modules/l3.png">
</div>
</a>
</div>
</div>
<div class="carousel-item active"
style="min-height: 198.656px;">
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16"
style="float:left">
<a href="https://apps.odoo.com/apps/modules/16.0/odoo_dynamic_dashboard/"
target="_blank">
<div style="border-radius:10px">
<img class="img img-responsive center-block"
style="border-radius: 0px;"
src="assets/modules/l4.png">
</div>
</a>
</div>
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16"
style="float:left">
<a href="https://apps.odoo.com/apps/modules/16.0/custom_gantt_view/"
target="_blank">
<div style="border-radius:10px">
<img class="img img-responsive center-block"
style="border-radius: 0px;"
src="assets/modules/l5.png">
</div>
</a>
</div>
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16"
style="float:left">
<a href="https://apps.odoo.com/apps/modules/16.0/pos_credit_limit/"
target="_blank">
<div style="border-radius:10px">
<img class="img img-responsive center-block"
style="border-radius: 0px;"
src="assets/modules/l6.png">
</div>
</a>
</div>
</div>
</div>
<!-- Left and right controls -->
<a class="carousel-control-prev" href="#demo1" data-slide="prev"
style="width:35px; color:#000"> <span
class="carousel-control-prev-icon"><i
class="fa fa-chevron-left"
style="font-size:24px"></i></span>
</a> <a class="carousel-control-next" href="#demo1"
data-slide="next" style="width:35px; color:#000">
<span class="carousel-control-next-icon"><i
class="fa fa-chevron-right"
style="font-size:24px"></i></span>
</a>
</div>
</div>
</div>
<!-- END OF RELATED PRODUCTS -->
<!-- OUR SERVICES -->
<div class="d-flex align-items-center"
style="border-bottom: 2px solid #714B67; padding: 15px 0px;">
<div class="d-flex justify-content-center align-items-center mr-2"
style="background-color: #F5F5F5; border-radius: 0px; width: 40px; height: 40px;">
<img src="assets/misc/star.png"/>
</div>
<h2 class="mt-2"
style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;">
Our Services
</h2>
</div>
<div class="container my-5">
<div class="row">
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4">
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
style="background-color: #1dd1a1 !important; border-radius: 15px !important; height: 80px; width: 80px;">
<img src="assets/icons/cogs.png" class="img-responsive"
height="48px" width="48px">
</div>
<h6 class="text-center"
style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;">
Odoo
Customization</h6>
</div>
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4">
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
style="background-color: #ff6b6b !important; border-radius: 15px !important; height: 80px; width: 80px;">
<img src="assets/icons/wrench.png" class="img-responsive"
height="48px" width="48px">
</div>
<h6 class="text-center"
style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;">
Odoo
Implementation</h6>
</div>
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4">
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
style="background-color: #6462CD !important; border-radius: 15px !important; height: 80px; width: 80px;">
<img src="assets/icons/lifebuoy.png" class="img-responsive"
height="48px" width="48px">
</div>
<h6 class="text-center"
style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;">
Odoo
Support</h6>
</div>
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4">
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
style="background-color: #ffa801 !important; border-radius: 15px !important; height: 80px; width: 80px;">
<img src="assets/icons/user.png" class="img-responsive"
height="48px" width="48px">
</div>
<h6 class="text-center"
style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;">
Hire
Odoo
Developer</h6>
</div>
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4">
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
style="background-color: #54a0ff !important; border-radius: 15px !important; height: 80px; width: 80px;">
<img src="assets/icons/puzzle.png" class="img-responsive"
height="48px" width="48px">
</div>
<h6 class="text-center"
style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;">
Odoo
Integration</h6>
</div>
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4">
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
style="background-color: #6d7680 !important; border-radius: 15px !important; height: 80px; width: 80px;">
<img src="assets/icons/update.png" class="img-responsive"
height="48px" width="48px">
</div>
<h6 class="text-center"
style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;">
Odoo
Migration</h6>
</div>
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4">
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
style="background-color: #786fa6 !important; border-radius: 15px !important; height: 80px; width: 80px;">
<img src="assets/icons/consultation.png" class="img-responsive"
height="48px" width="48px">
</div>
<h6 class="text-center"
style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;">
Odoo
Consultancy</h6>
</div>
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4">
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
style="background-color: #f8a5c2 !important; border-radius: 15px !important; height: 80px; width: 80px;">
<img src="assets/icons/training.png" class="img-responsive"
height="48px" width="48px">
</div>
<h6 class="text-center"
style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;">
Odoo
Implementation</h6>
</div>
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4">
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
style="background-color: #e6be26 !important; border-radius: 15px !important; height: 80px; width: 80px;">
<img src="assets/icons/license.png" class="img-responsive"
height="48px" width="48px">
</div>
<h6 class="text-center"
style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;">
Odoo
Licensing Consultancy</h6>
</div>
</div>
</div>
<!--END OF 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 -->

27
purchase_recurring_orders/views/purchase_order_view.xml

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!--Generate Agreement Button -->
<record id="view_purchase_order_form" model="ir.ui.view">
<field name="name">purchase.order.inherit.form</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_purchases_order_filter" model="ir.ui.view">
<field name="name">purchase.order.list.select.inherit</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>

183
purchase_recurring_orders/views/recurring_orders_view.xml

@ -0,0 +1,183 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Purchase Agreement Renewal tree View-->
<record model="ir.ui.view"
id="view_purchase_agreement_renewal_tree">
<field name="name">purchase.agreement.renewal.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="view_purchase_recurring_agreement_tree">
<field name="name">purchase.recurring.agreement.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="view_purchase_recurring_agreement_form"
model="ir.ui.view">
<field name="name">purchase.recurring.agreement.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"
states="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="%(action_agreement_renewal_wizard)d"
type="action"
string="Renew Agreement"
attrs="{'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)"
attrs="{'readonly':[('state','!=','empty')]}"/>
<field name="next_expiration_date"/>
<field name="prolong_unit"
attrs="{'required': [('prolong', '!=', 'fixed')]}"/>
</group>
<group>
<group>
<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>
<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="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"
attrs="{'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="view_purchases_recurring_agreement_select" model="ir.ui.view">
<field name="name">purchase.recurring.agreement.select</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="action_recurring_orders_agreement">
<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="action_recurring_orders_agreement"
sequence="4"/>
</odoo>

27
purchase_recurring_orders/views/res_partner_view.xml

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Agreement 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-->
<record id="res_partner_view_agreement_button" model="ir.ui.view">
<field name="name">res.partner.view.agreement.button</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>

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: Unnimaya C O (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

56
purchase_recurring_orders/wizard/renew_wizard.py

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

46
purchase_recurring_orders/wizard/renew_wizard_view.xml

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Purchase Agreement Renewal Wizard Form View -->
<record model="ir.ui.view" id="view_agreement_renewal_wizard_form">
<field name="model">agreement.renewal.wizard</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="action_agreement_renewal_wizard"
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.wizard</field>
<field name="view_mode">kanban,list,form</field>
<field name="view_id"
ref="view_agreement_renewal_wizard_form"/>
<field name="target">new</field>
</record>
</odoo>
Loading…
Cancel
Save