diff --git a/purchase_recurring_orders/README.rst b/purchase_recurring_orders/README.rst new file mode 100755 index 000000000..a9f166719 --- /dev/null +++ b/purchase_recurring_orders/README.rst @@ -0,0 +1,43 @@ +.. image:: https://img.shields.io/badge/license-AGPL--3-blue.svg + :target: https://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 + +Purchase Recurring Orders +========================= +This module allows you to create recurring orders for purchases. + +Configuration +============= +No additional configuration required + +Company +------- +* `Cybrosys Techno Solutions `__ + +Credits +------- +Developer: (V16) Unnimaya C O, + (V17) Ayana K P, +Contact: odoo@cybrosys.com + +Contacts +-------- +* Mail Contact : odoo@cybrosys.com +* Website : https://cybrosys.com + +Bug Tracker +----------- +Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. + +Maintainer +========== +.. image:: https://cybrosys.com/images/logo.png + :target: https://cybrosys.com + +This module is maintained by Cybrosys Technologies. + +For support and more information, please visit `Our Website `__ + +Further information +=================== +HTML Description: ``__ diff --git a/purchase_recurring_orders/__init__.py b/purchase_recurring_orders/__init__.py new file mode 100644 index 000000000..d2811169e --- /dev/null +++ b/purchase_recurring_orders/__init__.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +################################################################################ +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies(). +# Author: Ayana KP (odoo@cybrosys.com) +# +# You can modify it under the terms of the GNU AFFERO +# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. +# +# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE +# (AGPL v3) along with this program. +# If not, see . +# +################################################################################ +from . import models +from . import wizard diff --git a/purchase_recurring_orders/__manifest__.py b/purchase_recurring_orders/__manifest__.py new file mode 100644 index 000000000..d84a92ee9 --- /dev/null +++ b/purchase_recurring_orders/__manifest__.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +################################################################################ +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies(). +# Author: Ayana KP (odoo@cybrosys.com) +# +# You can modify it under the terms of the GNU AFFERO +# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. +# +# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE +# (AGPL v3) along with this program. +# If not, see . +# +################################################################################ +{ + 'name': 'Purchase Recurring Orders', + 'version': '17.0.1.0.0', + 'category': 'Inventory,Purchases', + 'summary': """Helps to create Purchase Recurring orders""", + 'description': """This module Helps to create recurring orders for + Purchases based on the information provided.""", + 'author': 'Cybrosys Techno Solutions', + 'company': 'Cybrosys Techno Solutions', + 'maintainer': 'Cybrosys Techno Solutions', + 'website': 'https://www.cybrosys.com', + 'depends': ['purchase'], + 'data': [ + 'security/ir.model.access.csv', + 'data/ir_cron_data.xml', + 'data/ir_sequence_data.xml', + 'wizard/renew_wizard_views.xml', + 'views/recurring_orders_views.xml', + 'views/purchase_order_views.xml', + 'views/res_partner_views.xml', + ], + 'images': ['static/description/banner.png'], + 'license': 'AGPL-3', + 'installable': True, + 'auto_install': False, + 'application': False, +} diff --git a/purchase_recurring_orders/data/ir_cron_data.xml b/purchase_recurring_orders/data/ir_cron_data.xml new file mode 100644 index 000000000..a6ac6e78c --- /dev/null +++ b/purchase_recurring_orders/data/ir_cron_data.xml @@ -0,0 +1,44 @@ + + + + + + Prolongation Check for Recurring Orders Agreements + + + ir.actions.server + code + model.revise_agreements_expirations_planned() + + 1 + + days + -1 + + + + Confirm Current Orders + + ir.actions.server + code + model.confirm_current_orders_planned() + 1 + + days + -1 + + + + Generate Recurring Orders for Next Year + + ir.actions.server + code + model.generate_next_orders_planned() + 1 + days + -1 + + + diff --git a/purchase_recurring_orders/data/ir_sequence_data.xml b/purchase_recurring_orders/data/ir_sequence_data.xml new file mode 100644 index 000000000..7983fe818 --- /dev/null +++ b/purchase_recurring_orders/data/ir_sequence_data.xml @@ -0,0 +1,12 @@ + + + + + + Agreement Sequence + purchase.r_o.agreement.sequence + 4 + AG-%(y)s- + + + diff --git a/purchase_recurring_orders/doc/RELEASE_NOTES.md b/purchase_recurring_orders/doc/RELEASE_NOTES.md new file mode 100644 index 000000000..fbb7c206c --- /dev/null +++ b/purchase_recurring_orders/doc/RELEASE_NOTES.md @@ -0,0 +1,7 @@ +## Module + +#### 15.03.2024 +#### Version 17.0.1.0.0 +#### ADD + +- Initial commit for Purchase Recurring Orders diff --git a/purchase_recurring_orders/models/__init__.py b/purchase_recurring_orders/models/__init__.py new file mode 100644 index 000000000..6d11a5619 --- /dev/null +++ b/purchase_recurring_orders/models/__init__.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +################################################################################ +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies(). +# Author: Ayana KP (odoo@cybrosys.com) +# +# You can modify it under the terms of the GNU AFFERO +# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. +# +# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE +# (AGPL v3) along with this program. +# If not, see . +# +################################################################################ +from . import purchase_agreement_renewal +from . import purchase_order +from . import purchase_recurring_agreement +from . import recurring_agreement_line diff --git a/purchase_recurring_orders/models/purchase_agreement_renewal.py b/purchase_recurring_orders/models/purchase_agreement_renewal.py new file mode 100644 index 000000000..1a0f4bcf7 --- /dev/null +++ b/purchase_recurring_orders/models/purchase_agreement_renewal.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +################################################################################ +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies(). +# Author: Ayana KP (odoo@cybrosys.com) +# +# You can modify it under the terms of the GNU AFFERO +# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. +# +# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE +# (AGPL v3) along with this program. +# If not, see . +# +################################################################################ +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') diff --git a/purchase_recurring_orders/models/purchase_order.py b/purchase_recurring_orders/models/purchase_order.py new file mode 100644 index 000000000..6653b1b2e --- /dev/null +++ b/purchase_recurring_orders/models/purchase_order.py @@ -0,0 +1,98 @@ +# -*- coding: utf-8 -*- +################################################################################ +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies(). +# Author: Ayana KP (odoo@cybrosys.com) +# +# You can modify it under the terms of the GNU AFFERO +# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. +# +# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE +# (AGPL v3) along with this program. +# If not, see . +# +################################################################################ +from odoo import api, fields, models + + +class PurchaseOrder(models.Model): + """purchase Order Inherited""" + _inherit = 'purchase.order' + + @api.model + def _prepare_agreement_vals(self, order): + """ Method for creating agreement values""" + return { + 'name': order.name, + 'partner_id': order.partner_id.id, + 'company_id': order.company_id.id, + 'start_date': fields.Datetime.now(), + } + + @api.model + def _prepare_agreement_line_vals(self, order_ids, agreement): + """ Returns the Agreement Line Values in a Dictionary Format""" + return { + 'recurring_agreement_id': agreement.id, + 'product_id': order_ids.product_id.id, + 'quantity': order_ids.product_qty, + } + + def action_button_generate_agreement(self): + """Generates Purchase Recurring Agreement""" + agreements = [] + agreement_obj = self.env['purchase.recurring.agreement'] + agreement_line_obj = self.env['recurring.agreement.line'] + for purchase_order in self: + agreement_vals = self._prepare_agreement_vals(purchase_order) + agreement = agreement_obj.create(agreement_vals) + agreements.append(agreement) + for order_id in purchase_order.order_line: + agreement_line_vals = self._prepare_agreement_line_vals( + order_id, agreement) + agreement_line_obj.create(agreement_line_vals) + if len(agreements) == 1: + view = self.env.ref( + 'purchase_recurring_orders.' + 'purchase_recurring_agreement_view_form') + return { + 'type': 'ir.actions.act_window', + 'view_mode': 'form', + 'res_model': 'purchase.recurring.agreement', + 'views': [(view.id, 'form')], + 'view_id': view.id, + 'target': 'new', + 'res_id': agreement[0].id, + 'nodestroy': True, + } + return True + + from_agreement = fields.Boolean( + string='From Agreement?', copy=False, + help='This field indicates if the purchase order comes from ' + 'an agreement.') + recurring_agreement_id = fields.Many2one('purchase.recurring.agreement', + string='Agreement Reference', + help="this indicates the Purchase " + "Agreement", + ondelete='restrict') + + def view_order(self): + """Returns the Corresponding Order""" + return { + 'view_type': 'form', + 'view_mode': 'form', + 'res_model': 'purchase.order', + 'context': self.env.context, + 'res_id': self[:1].id, + 'view_id': [self.env.ref('purchase.purchase_order_form').id], + 'type': 'ir.actions.act_window', + 'nodestroy': True + } diff --git a/purchase_recurring_orders/models/purchase_recurring_agreement.py b/purchase_recurring_orders/models/purchase_recurring_agreement.py new file mode 100644 index 000000000..a684e8f72 --- /dev/null +++ b/purchase_recurring_orders/models/purchase_recurring_agreement.py @@ -0,0 +1,421 @@ +# -*- coding: utf-8 -*- +################################################################################ +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies(). +# Author: Ayana KP (odoo@cybrosys.com) +# +# You can modify it under the terms of the GNU AFFERO +# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. +# +# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE +# (AGPL v3) along with this program. +# If not, see . +# +################################################################################ +from datetime import timedelta +from dateutil.relativedelta import relativedelta +from odoo import api, exceptions, fields, models, _ + + +class PurchaseRecurringAgreement(models.Model): + """Model for generating purchase recurring agreement""" + _name = 'purchase.recurring.agreement' + _inherit = 'mail.thread' + _description = "Purchase Recurring Agreement" + + @api.model + def _get_next_term_date(self, date, unit, interval): + """Returns the Next Term Date""" + if unit == 'days': + date = date + timedelta(days=interval) + elif unit == 'weeks': + date = date + timedelta(weeks=interval) + elif unit == 'months': + date = date + relativedelta(months=interval) + elif unit == 'years': + date = date + relativedelta(years=interval) + return date + + def _compute_next_expiration_date(self): + """Calculates the Next Expiration Date According to the Prolongation + Unit Chosen""" + for agreement in self: + if agreement.prolong == 'fixed': + agreement.next_expiration_date = agreement.end_date + elif agreement.prolong == 'unlimited': + now = fields.Date.from_string(fields.Datetime.today()) + date = self._get_next_term_date( + fields.Date.from_string(agreement.start_date), + agreement.prolong_unit, agreement.prolong_interval) + while date < now: + date = self._get_next_term_date( + date, agreement.prolong_unit, + agreement.prolong_interval) + agreement.next_expiration_date = date + else: + agreement.next_expiration_date = self._get_next_term_date( + fields.Datetime.from_string( + agreement.last_renovation_date or + agreement.start_date), + agreement.prolong_unit, agreement.prolong_interval) + + def _default_company_id(self): + """Returns the Current Company Id""" + company_model = self.env['res.company'] + company_id = company_model._company_default_get('purchase') + return company_model.browse(company_id.id) + + name = fields.Char( + string='Name', size=100, index=True, required=True, + help='Name that Helps to Identify the Agreement') + number = fields.Char( + string='Agreement Number', index=True, size=32, copy=False, + help="Number of Agreement. Keep Empty to Get the Number Assigned by a " + "Sequence.") + active = fields.Boolean( + string='Active', default=True, + help='Uncheck this Field, Quotas are not Generated') + partner_id = fields.Many2one('res.partner', string='Supplier', index=True, + change_default=True, required=True, + help="Supplier You are Making the " + "Agreement with") + company_id = fields.Many2one('res.company', string='Company', required=True, + help="Company that Signs the Agreement", + default=_default_company_id) + start_date = fields.Date( + string='Start Date', index=True, copy=False, + help="Beginning of the Agreement. Keep Empty to Use the Current Date") + prolong = fields.Selection( + selection=[('recurrent', 'Renewable Fixed Term'), + ('unlimited', 'Unlimited Term'), + ('fixed', 'Fixed Term')], + string='Prolongation', default='unlimited', + help="Sets the term of the agreement. 'Renewable fixed term': It sets " + "a fixed term, but with possibility of manual renew; 'Unlimited " + "term': Renew is made automatically; 'Fixed term': The term is " + "fixed and there is no possibility to renew.") + end_date = fields.Date( + string='End date', help="End Date of the Agreement") + prolong_interval = fields.Integer( + string='Interval', default=1, + help="Interval in time units to prolong the agreement until new " + "renewable (that is automatic for unlimited term, manual for " + "renewable fixed term).") + prolong_unit = fields.Selection( + selection=[('days', 'Days'), + ('weeks', 'Weeks'), + ('months', 'Months'), + ('years', 'Years')], + string='Interval Unit', default='years', + help='Time unit for the prolongation interval') + agreement_line_ids = fields.One2many('recurring.agreement.line', + inverse_name='recurring_agreement_id', + string='Agreement Lines') + order_ids = fields.One2many('purchase.order', copy=False, + inverse_name='recurring_agreement_id', + string='Orders', readonly=True) + renewal_ids = fields.One2many('purchase.agreement.renewal', copy=False, + inverse_name='recurring_agreement_id', + string='Renewal Lines', + readonly=True) + last_renovation_date = fields.Datetime( + string='Last Renovation Date', + help="Last date when agreement was renewed (same as start date if not " + "renewed)") + next_expiration_date = fields.Datetime( + compute="_compute_next_expiration_date", + help="Date when agreement will expired ", + string='Next Expiration Date') + state = fields.Selection( + selection=[('empty', 'Without Orders'), + ('first', 'First Order Created'), + ('orders', 'With Orders')], + string='State', help="Indicates the state of recurring agreement", + readonly=True, default='empty') + renewal_state = fields.Selection( + selection=[('not_renewed', 'Agreement not Renewed'), + ('renewed', 'Agreement Renewed')], + string='Renewal State', + help="Renewal Status of the Recurring agreement", readonly=True, + default='not_renewed') + notes = fields.Text('Notes', help="Notes regarding Renewal agreement") + order_count = fields.Integer(compute='_compute_order_count', + help="Indicates the No. of Orders Generated " + "with this Agreement") + + _sql_constraints = [ + ('number_uniq', 'unique(number)', 'Agreement Number Must be Unique !'), + ] + + def get_orders(self): + """Returns All Orders Generated from the Agreement""" + self.ensure_one() + return { + 'type': 'ir.actions.act_window', + 'name': 'Orders', + 'views': [[False, 'tree'], [False, 'form']], + 'res_model': 'purchase.order', + 'domain': [('recurring_agreement_id', '=', self.id)], + 'context': "{'create': False}" + } + + def _compute_order_count(self): + """Finds the count of orders generated from the Agreement""" + for record in self: + record.order_count = self.env['purchase.order'].search_count( + [('recurring_agreement_id', '=', self.id)]) + + @api.constrains('start_date', 'end_date') + def _check_dates(self): + """Method for ensuring start date will be always less than + or equal to end date""" + for record in self: + if record.end_date and record.end_date < record.start_date: + raise exceptions.Warning( + _('Agreement End Date must be Greater than Start Date')) + + @api.model + def create(self, vals): + """Function that supering create function""" + if not vals.get('start_date'): + vals['start_date'] = fields.Datetime.today() + if not vals.get('number'): + vals['number'] = self.env['ir.sequence'].get( + 'purchase.r_o.agreement.sequence') + return super().create(vals) + + def write(self, vals): + """Function that supering write function""" + value = super().write(vals) + if (any(vals.get(rec) is not None for rec in + ['active', 'number', 'agreement_line_ids', 'prolong', + 'end_date', + 'prolong_interval', 'prolong_unit', 'partner_id'])): + self.unlink_orders(fields.Datetime.today()) + return value + + @api.returns('self', lambda value: value.id) + def copy(self, default=None): + default = dict(default or {}) + if 'name' not in default: + default['name'] = _("%s (Copy)") % self.name + return super().copy(default=default) + + def unlink(self): + """Function that supering unlink function which will unlink Self and + the Current record""" + for agreement in self: + if any(agreement.mapped('order_ids')): + raise exceptions.Warning( + _('You Cannot Remove Agreements with Confirmed Orders!')) + self.unlink_orders(fields.Datetime.from_string(fields.Datetime.today())) + return models.Model.unlink(self) + + @api.onchange('start_date') + def onchange_start_date(self, start_date=False): + """Method for updating last renovation date""" + if not start_date: + return {} + result = {'value': {'last_renovation_date': start_date}} + return result + + @api.model + def revise_agreements_expirations_planned(self): + """Method for changing the prolong as unlimited""" + for agreement in self.search([('prolong', '=', 'unlimited')]): + if agreement.next_expiration_date <= fields.Datetime.today(): + agreement.write({'prolong': 'unlimited'}) + return True + + @api.model + def _prepare_purchase_order_vals(self, agreement, date): + """Creates purchase order values""" + # Order Values + order_vals = {'date_order': date, 'origin': agreement.number, + 'partner_id': agreement.partner_id.id, + 'state': 'draft', 'company_id': agreement.company_id.id, + 'from_agreement': True, + 'recurring_agreement_id': agreement.id, + 'date_planned': date, + 'fiscal_position_id': self.env[ + 'account.fiscal.position'].with_context( + company_id=agreement.company_id.id). + _get_fiscal_position(agreement.partner_id), + 'payment_term_id': agreement.partner_id. + property_supplier_payment_term_id.id, + 'currency_id': agreement.partner_id. + property_purchase_currency_id.id or + self.env.user.company_id.currency_id.id, + 'user_id': agreement.partner_id.user_id.id} + return order_vals + + @api.model + def _prepare_purchase_order_line_vals(self, agreement_line_ids, order): + """Returns the Purchase Order Line Values as a Dictionary Which can be + Used While creating the Purchase Order""" + product_lang = agreement_line_ids.product_id.with_context({ + 'lang': order.partner_id.lang, + 'partner_id': order.partner_id.id, + }) + fpos = order.fiscal_position_id + # Order Line Values as a Dictionary + order_line_vals = { + 'order_id': order.id, + 'product_id': agreement_line_ids.product_id.id, + 'product_qty': agreement_line_ids.quantity, + 'date_planned': order.date_planned, + 'price_unit': agreement_line_ids.product_id. + _get_tax_included_unit_price( + order.company_id, + order.currency_id, + order.date_order, + 'purchase', + fiscal_position=order.fiscal_position_id, + product_uom=agreement_line_ids.product_id.uom_po_id), + 'product_uom': agreement_line_ids.product_id.uom_po_id.id or + agreement_line_ids.product_id.uom_id.id, + 'name': product_lang.display_name, + 'taxes_id': fpos.map_tax( + agreement_line_ids.product_id.supplier_taxes_id.filtered( + lambda r: r.company_id.id == self.company_id.id).ids) + } + # product price changed if specific price is added + if agreement_line_ids.specific_price: + order_line_vals['price_unit'] = agreement_line_ids.specific_price + order_line_vals['taxes_id'] = [ + (6, 0, tuple(order_line_vals['taxes_id']))] + # product price changed if specific price is added + if agreement_line_ids.additional_description: + order_line_vals['name'] += " %s" % ( + agreement_line_ids.additional_description) + return order_line_vals + + def create_order(self, date, agreement_lines): + """Create Purchase Order from Recurring Agreement """ + self.ensure_one() + order_line_obj = self.env['purchase.order.line'].with_context( + company_id=self.company_id.id) + order_vals = self._prepare_purchase_order_vals(self, date) + order = self.env['purchase.order'].create(order_vals) + for agreement_line in agreement_lines: + # Create Purchase Order Line Values + order_line_vals = self._prepare_purchase_order_line_vals( + agreement_line, order) + order_line_obj.create(order_line_vals) + agreement_lines.write({'last_order_date': fields.Datetime.today()}) + if self.state != 'orders': + self.state = 'orders' + return order + + def _get_next_order_date(self, line, start_date): + """Return The date of Next Purchase order generated from the + Agreement""" + self.ensure_one() + next_date = fields.Datetime.from_string(self.start_date) + while next_date <= start_date: + next_date = self._get_next_term_date( + next_date, line.ordering_unit, line.ordering_interval) + return next_date + + def generate_agreement_orders(self, start_date, end_date): + """Method for generating agreement orders""" + self.ensure_one() + if not self.active: + return + lines_to_order = {} + # Get next expiration date + exp_date = fields.Datetime.from_string(self.next_expiration_date) + if exp_date < end_date and self.prolong != 'unlimited': + end_date = exp_date + for line in self.agreement_line_ids: + if not line.active_chk: + continue + # Get Date of Next Order + next_order_date = self._get_next_order_date(line, start_date) + while next_order_date <= end_date: + if not lines_to_order.get(next_order_date): + lines_to_order[next_order_date] = self.env[ + 'recurring.agreement.line'] + lines_to_order[next_order_date] |= line + next_order_date = self._get_next_order_date( + line, next_order_date) + dates = lines_to_order.keys() + sorted(dates) + for date in dates: + order = self.order_ids.filtered( + lambda x: ( + fields.Date.to_string( + fields.Datetime.from_string(x.date_order)) == + fields.Date.to_string(date))) + if not order: + self.create_order( + fields.Datetime.to_string(date), lines_to_order[date]) + + def generate_initial_order(self): + """This will generate the Initial purchase Order from the Purchase + Agreement""" + self.ensure_one() + agreement_lines = self.mapped('agreement_line_ids').filtered( + 'active_chk') + order = self.create_order(self.start_date, agreement_lines) + self.write({'state': 'first'}) + order.button_confirm() + return { + 'domain': "[('id', '=', %s)]" % order.id, + 'view_type': 'form', + 'view_mode': 'form', + 'res_model': 'purchase.order', + 'context': self.env.context, + 'res_id': order.id, + 'view_id': [self.env.ref('purchase.purchase_order_form').id], + 'type': 'ir.actions.act_window', + 'nodestroy': True + } + + @api.model + def generate_next_orders_planned(self, years=1, start_date=None): + """Method for generating the planned orders""" + if start_date: + start_date = fields.Datetime.from_string(start_date) + self.search([]).generate_next_orders( + years=years, start_date=start_date) + + def generate_next_year_orders(self): + """This will Generate Orders for Next year""" + return self.generate_next_orders(years=1) + + def generate_next_orders(self, years=1, start_date=None): + if not start_date: + start_date = fields.Datetime.from_string(fields.Date.today()) + end_date = start_date + relativedelta(years=years) + for agreement in self: + agreement.generate_agreement_orders(start_date, end_date) + return True + + @api.model + def confirm_current_orders_planned(self): + """This will Confirm All Orders satisfying the Domain""" + tomorrow = fields.Date.to_string( + fields.Datetime.from_string(fields.Datetime.today()) + timedelta( + days=1)) + orders = self.env['purchase.order'].search([ + ('recurring_agreement_id', '!=', False), + ('state', 'in', ('draft', 'sent')), + ('date_order', '<', tomorrow) + ]) + for order in orders: + order.signal_workflow('order_confirm') + + def unlink_orders(self, start_date): + """ Remove the relation between ``self`` and the related record.""" + orders = self.mapped('order_ids').filtered( + lambda x: (x.state in ('draft', 'sent') and + x.date_order >= start_date)) + orders.unlink() diff --git a/purchase_recurring_orders/models/recurring_agreement_line.py b/purchase_recurring_orders/models/recurring_agreement_line.py new file mode 100644 index 000000000..eaebd6b12 --- /dev/null +++ b/purchase_recurring_orders/models/recurring_agreement_line.py @@ -0,0 +1,91 @@ +# -*- coding: utf-8 -*- +################################################################################ +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies(). +# Author: Ayana KP (odoo@cybrosys.com) +# +# You can modify it under the terms of the GNU AFFERO +# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. +# +# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE +# (AGPL v3) along with this program. +# If not, see . +# +################################################################################ +from odoo import api, fields, models +from odoo.addons.base.models.decimal_precision import dp + + +class RecurringAgreementLine(models.Model): + """Model generating purchase recurring agreement line""" + _name = 'recurring.agreement.line' + _description = 'Recurring Agreement Line' + + active_chk = fields.Boolean( + string='Active', default=True, + help='Unchecking this field, this quota is not generated') + recurring_agreement_id = fields.Many2one( + 'purchase.recurring.agreement', + string='Agreement Reference', + help="The Corresponding Purchase Order Agreement", + ondelete='cascade') + product_id = fields.Many2one('product.product', string='Product', + ondelete='restrict', + required=True) + uom_id = fields.Many2one(related='product_id.product_tmpl_id.uom_id', + help="UOM of the product", string="Uom") + + name = fields.Char( + related="product_id.name", string='Description', + help="Description of the Product") + additional_description = fields.Char( + string='Description', size=30, + help='Additional description that will be added to the product ' + 'description on orders.') + quantity = fields.Float( + string='Quantity', required=True, help='Quantity of the Product', + default=1.0) + ordering_interval = fields.Integer( + string='Interval', required=True, default=1, + help="Interval in time units for making an order of this product") + ordering_unit = fields.Selection( + selection=[('days', 'Days'), + ('weeks', 'Weeks'), + ('months', 'Months'), + ('years', 'Years')], + string='Interval Unit', required=True, + help="It indicated the Recurring Time Unit", default='months') + last_order_date = fields.Datetime( + string='Last Order', help='Date of the last Purchase order generated') + specific_price = fields.Float( + string='Specific Price', + digits_compute=dp.get_precision('Purchase Price'), + help='Specific price for this product. Keep empty to use the list ' + 'price while generating order') + list_price = fields.Float( + related='product_id.list_price', string="List Price", readonly=True, + help='Price of product in purchase order lines') + + _sql_constraints = [ + ('line_qty_zero', 'CHECK (quantity > 0)', + 'All product quantities must be greater than 0.\n'), + ('line_interval_zero', 'CHECK (ordering_interval > 0)', + 'All ordering intervals must be greater than 0.\n'), + ] + + @api.onchange('product_id') + def onchange_product_id(self, product_id=False): + """For getting product name""" + result = {} + if product_id: + product = self.env['product.product'].browse(product_id) + if product: + result['value'] = {'name': product['name']} + return result diff --git a/purchase_recurring_orders/security/ir.model.access.csv b/purchase_recurring_orders/security/ir.model.access.csv new file mode 100644 index 000000000..c055a257c --- /dev/null +++ b/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,agreement.renewal,model_agreement_renewal,purchase.group_purchase_user,1,1,1, \ No newline at end of file diff --git a/purchase_recurring_orders/static/description/assets/icons/capture (1).png b/purchase_recurring_orders/static/description/assets/icons/capture (1).png new file mode 100644 index 000000000..8824deafc Binary files /dev/null and b/purchase_recurring_orders/static/description/assets/icons/capture (1).png differ diff --git a/purchase_recurring_orders/static/description/assets/icons/check.png b/purchase_recurring_orders/static/description/assets/icons/check.png new file mode 100644 index 000000000..c8e85f51d Binary files /dev/null and b/purchase_recurring_orders/static/description/assets/icons/check.png differ diff --git a/purchase_recurring_orders/static/description/assets/icons/chevron.png b/purchase_recurring_orders/static/description/assets/icons/chevron.png new file mode 100644 index 000000000..2089293d6 Binary files /dev/null and b/purchase_recurring_orders/static/description/assets/icons/chevron.png differ diff --git a/purchase_recurring_orders/static/description/assets/icons/cogs.png b/purchase_recurring_orders/static/description/assets/icons/cogs.png new file mode 100644 index 000000000..95d0bad62 Binary files /dev/null and b/purchase_recurring_orders/static/description/assets/icons/cogs.png differ diff --git a/purchase_recurring_orders/static/description/assets/icons/consultation.png b/purchase_recurring_orders/static/description/assets/icons/consultation.png new file mode 100644 index 000000000..8319d4baa Binary files /dev/null and b/purchase_recurring_orders/static/description/assets/icons/consultation.png differ diff --git a/purchase_recurring_orders/static/description/assets/icons/ecom-black.png b/purchase_recurring_orders/static/description/assets/icons/ecom-black.png new file mode 100644 index 000000000..a9385ff13 Binary files /dev/null and b/purchase_recurring_orders/static/description/assets/icons/ecom-black.png differ diff --git a/purchase_recurring_orders/static/description/assets/icons/education-black.png b/purchase_recurring_orders/static/description/assets/icons/education-black.png new file mode 100644 index 000000000..3eb09b27b Binary files /dev/null and b/purchase_recurring_orders/static/description/assets/icons/education-black.png differ diff --git a/purchase_recurring_orders/static/description/assets/icons/hotel-black.png b/purchase_recurring_orders/static/description/assets/icons/hotel-black.png new file mode 100644 index 000000000..130f613be Binary files /dev/null and b/purchase_recurring_orders/static/description/assets/icons/hotel-black.png differ diff --git a/purchase_recurring_orders/static/description/assets/icons/img.png b/purchase_recurring_orders/static/description/assets/icons/img.png new file mode 100644 index 000000000..70197f477 Binary files /dev/null and b/purchase_recurring_orders/static/description/assets/icons/img.png differ diff --git a/purchase_recurring_orders/static/description/assets/icons/license.png b/purchase_recurring_orders/static/description/assets/icons/license.png new file mode 100644 index 000000000..a5869797e Binary files /dev/null and b/purchase_recurring_orders/static/description/assets/icons/license.png differ diff --git a/purchase_recurring_orders/static/description/assets/icons/lifebuoy.png b/purchase_recurring_orders/static/description/assets/icons/lifebuoy.png new file mode 100644 index 000000000..658d56ccc Binary files /dev/null and b/purchase_recurring_orders/static/description/assets/icons/lifebuoy.png differ diff --git a/purchase_recurring_orders/static/description/assets/icons/manufacturing-black.png b/purchase_recurring_orders/static/description/assets/icons/manufacturing-black.png new file mode 100644 index 000000000..697eb0e9f Binary files /dev/null and b/purchase_recurring_orders/static/description/assets/icons/manufacturing-black.png differ diff --git a/purchase_recurring_orders/static/description/assets/icons/photo-capture.png b/purchase_recurring_orders/static/description/assets/icons/photo-capture.png new file mode 100644 index 000000000..06c111758 Binary files /dev/null and b/purchase_recurring_orders/static/description/assets/icons/photo-capture.png differ diff --git a/purchase_recurring_orders/static/description/assets/icons/pos-black.png b/purchase_recurring_orders/static/description/assets/icons/pos-black.png new file mode 100644 index 000000000..97c0f90c1 Binary files /dev/null and b/purchase_recurring_orders/static/description/assets/icons/pos-black.png differ diff --git a/purchase_recurring_orders/static/description/assets/icons/puzzle.png b/purchase_recurring_orders/static/description/assets/icons/puzzle.png new file mode 100644 index 000000000..65cf854e7 Binary files /dev/null and b/purchase_recurring_orders/static/description/assets/icons/puzzle.png differ diff --git a/purchase_recurring_orders/static/description/assets/icons/restaurant-black.png b/purchase_recurring_orders/static/description/assets/icons/restaurant-black.png new file mode 100644 index 000000000..4a35eb939 Binary files /dev/null and b/purchase_recurring_orders/static/description/assets/icons/restaurant-black.png differ diff --git a/purchase_recurring_orders/static/description/assets/icons/service-black.png b/purchase_recurring_orders/static/description/assets/icons/service-black.png new file mode 100644 index 000000000..301ab51cb Binary files /dev/null and b/purchase_recurring_orders/static/description/assets/icons/service-black.png differ diff --git a/purchase_recurring_orders/static/description/assets/icons/trading-black.png b/purchase_recurring_orders/static/description/assets/icons/trading-black.png new file mode 100644 index 000000000..9398ba2f1 Binary files /dev/null and b/purchase_recurring_orders/static/description/assets/icons/trading-black.png differ diff --git a/purchase_recurring_orders/static/description/assets/icons/training.png b/purchase_recurring_orders/static/description/assets/icons/training.png new file mode 100644 index 000000000..884ca024d Binary files /dev/null and b/purchase_recurring_orders/static/description/assets/icons/training.png differ diff --git a/purchase_recurring_orders/static/description/assets/icons/update.png b/purchase_recurring_orders/static/description/assets/icons/update.png new file mode 100644 index 000000000..ecbc5a01a Binary files /dev/null and b/purchase_recurring_orders/static/description/assets/icons/update.png differ diff --git a/purchase_recurring_orders/static/description/assets/icons/user.png b/purchase_recurring_orders/static/description/assets/icons/user.png new file mode 100644 index 000000000..6ffb23d9f Binary files /dev/null and b/purchase_recurring_orders/static/description/assets/icons/user.png differ diff --git a/purchase_recurring_orders/static/description/assets/icons/wrench.png b/purchase_recurring_orders/static/description/assets/icons/wrench.png new file mode 100644 index 000000000..6c04dea0f Binary files /dev/null and b/purchase_recurring_orders/static/description/assets/icons/wrench.png differ diff --git a/purchase_recurring_orders/static/description/assets/misc/Cybrosys R.png b/purchase_recurring_orders/static/description/assets/misc/Cybrosys R.png new file mode 100644 index 000000000..da4058087 Binary files /dev/null and b/purchase_recurring_orders/static/description/assets/misc/Cybrosys R.png differ diff --git a/purchase_recurring_orders/static/description/assets/misc/email.svg b/purchase_recurring_orders/static/description/assets/misc/email.svg new file mode 100644 index 000000000..15291cdc3 --- /dev/null +++ b/purchase_recurring_orders/static/description/assets/misc/email.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/purchase_recurring_orders/static/description/assets/misc/phone.svg b/purchase_recurring_orders/static/description/assets/misc/phone.svg new file mode 100644 index 000000000..b7bd7f251 --- /dev/null +++ b/purchase_recurring_orders/static/description/assets/misc/phone.svg @@ -0,0 +1,3 @@ + + + diff --git a/purchase_recurring_orders/static/description/assets/misc/star (1) 2.svg b/purchase_recurring_orders/static/description/assets/misc/star (1) 2.svg new file mode 100644 index 000000000..5ae9f507a --- /dev/null +++ b/purchase_recurring_orders/static/description/assets/misc/star (1) 2.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/purchase_recurring_orders/static/description/assets/misc/support (1) 1.svg b/purchase_recurring_orders/static/description/assets/misc/support (1) 1.svg new file mode 100644 index 000000000..7d37a8f30 --- /dev/null +++ b/purchase_recurring_orders/static/description/assets/misc/support (1) 1.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/purchase_recurring_orders/static/description/assets/misc/support-email.svg b/purchase_recurring_orders/static/description/assets/misc/support-email.svg new file mode 100644 index 000000000..eb70370d6 --- /dev/null +++ b/purchase_recurring_orders/static/description/assets/misc/support-email.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/purchase_recurring_orders/static/description/assets/misc/tick-mark.svg b/purchase_recurring_orders/static/description/assets/misc/tick-mark.svg new file mode 100644 index 000000000..2dbb40187 --- /dev/null +++ b/purchase_recurring_orders/static/description/assets/misc/tick-mark.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/purchase_recurring_orders/static/description/assets/misc/whatsapp 1.svg b/purchase_recurring_orders/static/description/assets/misc/whatsapp 1.svg new file mode 100644 index 000000000..0bfaf8fc6 --- /dev/null +++ b/purchase_recurring_orders/static/description/assets/misc/whatsapp 1.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/purchase_recurring_orders/static/description/assets/misc/whatsapp.svg b/purchase_recurring_orders/static/description/assets/misc/whatsapp.svg new file mode 100644 index 000000000..b618aea1d --- /dev/null +++ b/purchase_recurring_orders/static/description/assets/misc/whatsapp.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/purchase_recurring_orders/static/description/assets/modules/module_image (1).jpeg b/purchase_recurring_orders/static/description/assets/modules/module_image (1).jpeg new file mode 100644 index 000000000..686563477 Binary files /dev/null and b/purchase_recurring_orders/static/description/assets/modules/module_image (1).jpeg differ diff --git a/purchase_recurring_orders/static/description/assets/modules/module_image (2).jpeg b/purchase_recurring_orders/static/description/assets/modules/module_image (2).jpeg new file mode 100644 index 000000000..c4c4fdb15 Binary files /dev/null and b/purchase_recurring_orders/static/description/assets/modules/module_image (2).jpeg differ diff --git a/purchase_recurring_orders/static/description/assets/modules/module_image (3).jpg b/purchase_recurring_orders/static/description/assets/modules/module_image (3).jpg new file mode 100644 index 000000000..191937370 Binary files /dev/null and b/purchase_recurring_orders/static/description/assets/modules/module_image (3).jpg differ diff --git a/purchase_recurring_orders/static/description/assets/modules/module_image (4).jpeg b/purchase_recurring_orders/static/description/assets/modules/module_image (4).jpeg new file mode 100644 index 000000000..0e2040762 Binary files /dev/null and b/purchase_recurring_orders/static/description/assets/modules/module_image (4).jpeg differ diff --git a/purchase_recurring_orders/static/description/assets/modules/module_image (5).jpeg b/purchase_recurring_orders/static/description/assets/modules/module_image (5).jpeg new file mode 100644 index 000000000..0ed5f6b0b Binary files /dev/null and b/purchase_recurring_orders/static/description/assets/modules/module_image (5).jpeg differ diff --git a/purchase_recurring_orders/static/description/assets/modules/module_image.jpeg b/purchase_recurring_orders/static/description/assets/modules/module_image.jpeg new file mode 100644 index 000000000..fd64cf8ca Binary files /dev/null and b/purchase_recurring_orders/static/description/assets/modules/module_image.jpeg differ diff --git a/purchase_recurring_orders/static/description/assets/screenshots/1.png b/purchase_recurring_orders/static/description/assets/screenshots/1.png new file mode 100644 index 000000000..4d30a6926 Binary files /dev/null and b/purchase_recurring_orders/static/description/assets/screenshots/1.png differ diff --git a/purchase_recurring_orders/static/description/assets/screenshots/10.png b/purchase_recurring_orders/static/description/assets/screenshots/10.png new file mode 100644 index 000000000..d15e182e5 Binary files /dev/null and b/purchase_recurring_orders/static/description/assets/screenshots/10.png differ diff --git a/purchase_recurring_orders/static/description/assets/screenshots/11.png b/purchase_recurring_orders/static/description/assets/screenshots/11.png new file mode 100644 index 000000000..11c25393a Binary files /dev/null and b/purchase_recurring_orders/static/description/assets/screenshots/11.png differ diff --git a/purchase_recurring_orders/static/description/assets/screenshots/12.png b/purchase_recurring_orders/static/description/assets/screenshots/12.png new file mode 100644 index 000000000..a3526be45 Binary files /dev/null and b/purchase_recurring_orders/static/description/assets/screenshots/12.png differ diff --git a/purchase_recurring_orders/static/description/assets/screenshots/2.png b/purchase_recurring_orders/static/description/assets/screenshots/2.png new file mode 100644 index 000000000..e2e825eff Binary files /dev/null and b/purchase_recurring_orders/static/description/assets/screenshots/2.png differ diff --git a/purchase_recurring_orders/static/description/assets/screenshots/3.png b/purchase_recurring_orders/static/description/assets/screenshots/3.png new file mode 100644 index 000000000..82b8ee979 Binary files /dev/null and b/purchase_recurring_orders/static/description/assets/screenshots/3.png differ diff --git a/purchase_recurring_orders/static/description/assets/screenshots/4.png b/purchase_recurring_orders/static/description/assets/screenshots/4.png new file mode 100644 index 000000000..58392dc9f Binary files /dev/null and b/purchase_recurring_orders/static/description/assets/screenshots/4.png differ diff --git a/purchase_recurring_orders/static/description/assets/screenshots/6.png b/purchase_recurring_orders/static/description/assets/screenshots/6.png new file mode 100644 index 000000000..06ecb14f7 Binary files /dev/null and b/purchase_recurring_orders/static/description/assets/screenshots/6.png differ diff --git a/purchase_recurring_orders/static/description/assets/screenshots/7.png b/purchase_recurring_orders/static/description/assets/screenshots/7.png new file mode 100644 index 000000000..a0f3eedd2 Binary files /dev/null and b/purchase_recurring_orders/static/description/assets/screenshots/7.png differ diff --git a/purchase_recurring_orders/static/description/assets/screenshots/8.png b/purchase_recurring_orders/static/description/assets/screenshots/8.png new file mode 100644 index 000000000..5450a44e7 Binary files /dev/null and b/purchase_recurring_orders/static/description/assets/screenshots/8.png differ diff --git a/purchase_recurring_orders/static/description/assets/screenshots/9.png b/purchase_recurring_orders/static/description/assets/screenshots/9.png new file mode 100644 index 000000000..5ad365660 Binary files /dev/null and b/purchase_recurring_orders/static/description/assets/screenshots/9.png differ diff --git a/purchase_recurring_orders/static/description/assets/screenshots/hero-v17.gif b/purchase_recurring_orders/static/description/assets/screenshots/hero-v17.gif new file mode 100644 index 000000000..3eedb78f6 Binary files /dev/null and b/purchase_recurring_orders/static/description/assets/screenshots/hero-v17.gif differ diff --git a/purchase_recurring_orders/static/description/banner.png b/purchase_recurring_orders/static/description/banner.png new file mode 100644 index 000000000..3ecdf3c2b Binary files /dev/null and b/purchase_recurring_orders/static/description/banner.png differ diff --git a/purchase_recurring_orders/static/description/icon.png b/purchase_recurring_orders/static/description/icon.png new file mode 100644 index 000000000..e12cb7e81 Binary files /dev/null and b/purchase_recurring_orders/static/description/icon.png differ diff --git a/purchase_recurring_orders/static/description/index.html b/purchase_recurring_orders/static/description/index.html new file mode 100644 index 000000000..c09573e59 --- /dev/null +++ b/purchase_recurring_orders/static/description/index.html @@ -0,0 +1,661 @@ + + + + + + + Odoo App 3 Index + + + + + + + + +
+
+
+
+
+ +
+
+
+ Community +
+
+ Enterprise +
+
+ Odoo.sh +
+
+
+
+
+
+

+ Purchase Recurring Orders

+

+ Create Recurring Orders for Purchases Orders. +

+
+ +
+
+
+
+
+

Key Highlights +

+
+
+
+
+
+ +
+
+

Creates Recurring Orders for Purchase.

+

Generates Recurring Orders for purchase orders. +

+
+
+
+
+
+
+ +
+
+

Recurring Order Agreements.

+

Generates Recurring Orders Agreements for purchase. +

+
+
+
+
+
+
+ +
+
+

Create Next Year Orders at one click.

+

It is possible to create next year orders at one click. +

+
+
+
+
+
+
+ +
+
+

Scheduled action for creating Recurring Orders.

+

Can Automatically generate recurring orders on scheduled time. +

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

+ Menu for viewing and creating Recurring Order Agreements.

+
+
+
+
+
+
+ +
+
+

+ Create new recurring order agreement by filling the details in the form and add the products in Agreement Order Line. Click on the Generate Initial Order button for generating the first order

+
+
+
+
+
+
+ +
+
+

+ First Purchase Order Generated according to the details added in the Recurring purchase agreement.

+
+
+
+
+
+
+ +
+
+

+ We can see all orders created from the recurring purchase agreement from the smart button which shows the number of orders.

+
+
+
+
+
+
+ +
+
+

+ It is possible to create next year orders at one click.

+
+
+
+
+
+
+ +
+
+

+ The Number of Orders are increased.

+
+
+
+
+
+
+ +
+
+

+ All orders generated including Initial and Next year Orders.

+
+
+
+
+
+
+ +
+
+

+ If prolongation type changed to Renewable then a button appears for Renew Agreement.

+
+
+
+ +
+
+
+ +
+
+

+ Scheduled Action for generating Recurring Orders for Next Year

+
+
+
+
+
+
+
    +
  • + Creates recurring orders for purchas +
  • +
  • + Scheduled action for creating recurring orders. +
  • +
+
+
+
+
+
+
Version + 17.0.1.0.0|Released on:22th January 2024 +
+

+ + Initial Commit for Purchase Recurring Orders.

+
+
+
+
+
+
+
+

Related Products

+
+
+ +
+
+

Our Services

+ +
+
+
+
+
+
+
+
+ service-icon +
+
+

Odoo Customization

+
+
+
+
+
+
+ service-icon +
+
+

Odoo Implementation

+
+
+
+
+
+
+ service-icon +
+
+

Odoo Support

+
+
+
+
+
+
+ service-icon +
+
+

Hire Odoo Developer

+
+
+
+
+ +
+
+ service-icon +
+
+

Odoo Integration

+
+
+
+
+
+
+ service-icon +
+
+

Odoo Migration

+
+
+
+
+
+
+ service-icon +
+
+

Odoo Consultancy

+
+
+
+
+
+
+ service-icon +
+
+

Odoo Implementation

+
+
+
+
+
+
+ service-icon +
+
+

Odoo Licensing Consultancy

+
+
+
+
+
+
+

Our Industries

+ +
+
+
+
+
+
+ +

Trading

+

Easily procure and sell your products

+
+
+
+
+ +

POS

+

Easy configuration and convivial experience

+
+
+
+
+ +

Education

+

A platform for educational management

+
+
+
+
+ +

Manufacturing

+

Plan, track and schedule your operations

+
+
+
+
+ +

E-commerce & Website

+

Mobile friendly, awe-inspiring product pages

+
+
+
+
+ +

Service Management

+

Keep track of services and invoice

+
+
+
+
+ +

Restaurant

+

Run your bar or restaurant methodically

+
+
+
+
+ +

Hotel Management

+

An all-inclusive hotel management application

+
+
+
+
+
+
+

Support

+
+
+
+
+
+
+
+ +
+ Need + Help? +

Got questions or need help? Get in touch.

+
odoo@cybrosys.com +
+
+
+
+
+
+
+
+ +
+ WhatsApp +

Say hi to us on WhatsApp!

+
+91 + 99456767686
+
+
+
+
+
+
+
+
+ + + + + + diff --git a/purchase_recurring_orders/views/purchase_order_views.xml b/purchase_recurring_orders/views/purchase_order_views.xml new file mode 100644 index 000000000..67300b95a --- /dev/null +++ b/purchase_recurring_orders/views/purchase_order_views.xml @@ -0,0 +1,27 @@ + + + + + purchase.order.inherit.purchase.recurring.orders + purchase.order + + + + + + + purchase.order.inherit.purchase.recurring.orders + purchase.order + + + + + + + + + diff --git a/purchase_recurring_orders/views/recurring_orders_views.xml b/purchase_recurring_orders/views/recurring_orders_views.xml new file mode 100644 index 000000000..69850dd13 --- /dev/null +++ b/purchase_recurring_orders/views/recurring_orders_views.xml @@ -0,0 +1,183 @@ + + + + + purchase.agreement.renewal.view.tree + purchase.agreement.renewal + tree + + + + + + + + + + + purchase.recurring.agreement.view.tree + purchase.recurring.agreement + tree + + + + + + + + + + + + + + + + purchase.recurring.agreement.view.form + purchase.recurring.agreement + 6 + +
+
+
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+ +
+ + + + purchase.recurring.agreement.view.search + purchase.recurring.agreement + + + + + + + + + + + Recurring Order Agreement + purchase.recurring.agreement + tree,form + +

+ Click to set a new agreement. +

+

+ 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. +

+
+
+ + + diff --git a/purchase_recurring_orders/views/res_partner_views.xml b/purchase_recurring_orders/views/res_partner_views.xml new file mode 100644 index 000000000..9c9d6b2b7 --- /dev/null +++ b/purchase_recurring_orders/views/res_partner_views.xml @@ -0,0 +1,27 @@ + + + + + ir.actions.act_window + Purchase Recurring Agreement + purchase.recurring.agreement + tree,form + {'search_default_partner_id': active_id} + + + + res.partner.inherit.purchase.recurring.orders + res.partner + + + + +