@ -0,0 +1,24 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################## |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# Copyright (C) 2009-TODAY Cybrosys Technologies(<http://www.cybrosys.com>). |
||||
|
# Author: Jesni Banu(<http://www.cybrosys.com>) |
||||
|
# you can modify it under the terms of the GNU LESSER |
||||
|
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. |
||||
|
# |
||||
|
# It is forbidden to publish, distribute, sublicense, or sell copies |
||||
|
# of the Software or modified copies of the Software. |
||||
|
# |
||||
|
# 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 LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE |
||||
|
# GENERAL PUBLIC LICENSE (LGPL v3) along with this program. |
||||
|
# If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################### |
||||
|
from . import models |
||||
|
from . import wizard |
@ -0,0 +1,46 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################## |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# Copyright (C) 2009-TODAY Cybrosys Technologies(<http://www.cybrosys.com>). |
||||
|
# Author: Jesni Banu(<http://www.cybrosys.com>) |
||||
|
# you can modify it under the terms of the GNU LESSER |
||||
|
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. |
||||
|
# |
||||
|
# It is forbidden to publish, distribute, sublicense, or sell copies |
||||
|
# of the Software or modified copies of the Software. |
||||
|
# |
||||
|
# 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 LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE |
||||
|
# GENERAL PUBLIC LICENSE (LGPL v3) along with this program. |
||||
|
# If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################### |
||||
|
{ |
||||
|
'name': 'Purchase Recurring orders', |
||||
|
'version': '8.0.1.0.0', |
||||
|
'category': 'Purchase Management', |
||||
|
'summary': 'Recurring orders for Selected Purchase Order', |
||||
|
'description': 'This module allows you to create recurring orders for purchase.', |
||||
|
'author': 'Cybrosys Techno Solutions', |
||||
|
'company': 'Cybrosys Techno Solutions', |
||||
|
'website': 'http://www.cybrosys.com', |
||||
|
'depends': ['purchase'], |
||||
|
'data': [ |
||||
|
'security/ir.model.access.csv', |
||||
|
'data/recurring_orders_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', |
||||
|
'images': ['static/description/banner.jpg'], |
||||
|
'installable': True, |
||||
|
'auto_install': False, |
||||
|
'application': False, |
||||
|
} |
@ -0,0 +1,54 @@ |
|||||
|
<?xml version="1.0"?> |
||||
|
<openerp> |
||||
|
<data noupdate="1"> |
||||
|
|
||||
|
<record model="ir.sequence.type" id="seq_type_ro_agreement"> |
||||
|
<field name="name">Agreement sequence</field> |
||||
|
<field name="code">purchase.r_o.agreement.sequence</field> |
||||
|
</record> |
||||
|
|
||||
|
<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" eval="4"/> |
||||
|
<field name="prefix">AG-%(y)s-</field> |
||||
|
</record> |
||||
|
|
||||
|
<record model="ir.cron" id="cron_recurring_orders_prolong_check"> |
||||
|
<field name="name">Prolongation check for recurring orders agreements</field> |
||||
|
<field name="interval_number">1</field> |
||||
|
<field name="priority">10</field> |
||||
|
<field name="interval_type">days</field> |
||||
|
<field name="numbercall">-1</field> |
||||
|
<field name="doall" eval="False"/> |
||||
|
<field name="model" eval="'purchase.recurring_orders.agreement'"/> |
||||
|
<field name="function" eval="'revise_agreements_expirations_planned'"/> |
||||
|
<field name="args" eval="'()'"/> |
||||
|
</record> |
||||
|
|
||||
|
<record model="ir.cron" id="cron_recurring_orders_confirm_orders"> |
||||
|
<field name="name">Confirm current orders</field> |
||||
|
<field name="interval_number">1</field> |
||||
|
<field name="priority">10</field> |
||||
|
<field name="interval_type">days</field> |
||||
|
<field name="numbercall">-1</field> |
||||
|
<field name="doall" eval="False"/> |
||||
|
<field name="model" eval="'purchase.recurring_orders.agreement'"/> |
||||
|
<field name="function" eval="'confirm_current_orders_planned'"/> |
||||
|
<field name="args" eval="'()'"/> |
||||
|
</record> |
||||
|
|
||||
|
<record model="ir.cron" id="cron_recurring_orders_generate_orders"> |
||||
|
<field name="name">Generate recurring orders for next year</field> |
||||
|
<field name="interval_number">1</field> |
||||
|
<field name="priority">10</field> |
||||
|
<field name="interval_type">days</field> |
||||
|
<field name="numbercall">-1</field> |
||||
|
<field name="doall" eval="False"/> |
||||
|
<field name="model" eval="'purchase.recurring_orders.agreement'"/> |
||||
|
<field name="function" eval="'generate_next_orders_planned'"/> |
||||
|
<field name="args" eval="'(1, )'"/> |
||||
|
</record> |
||||
|
|
||||
|
</data> |
||||
|
</openerp> |
@ -0,0 +1,24 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################## |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# Copyright (C) 2009-TODAY Cybrosys Technologies(<http://www.cybrosys.com>). |
||||
|
# Author: Jesni Banu(<http://www.cybrosys.com>) |
||||
|
# you can modify it under the terms of the GNU LESSER |
||||
|
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. |
||||
|
# |
||||
|
# It is forbidden to publish, distribute, sublicense, or sell copies |
||||
|
# of the Software or modified copies of the Software. |
||||
|
# |
||||
|
# 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 LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE |
||||
|
# GENERAL PUBLIC LICENSE (LGPL v3) along with this program. |
||||
|
# If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################### |
||||
|
from . import recurring_orders |
||||
|
from . import purchase_order |
@ -0,0 +1,93 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################## |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# Copyright (C) 2009-TODAY Cybrosys Technologies(<http://www.cybrosys.com>). |
||||
|
# Author: Jesni Banu(<http://www.cybrosys.com>) |
||||
|
# you can modify it under the terms of the GNU LESSER |
||||
|
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. |
||||
|
# |
||||
|
# It is forbidden to publish, distribute, sublicense, or sell copies |
||||
|
# of the Software or modified copies of the Software. |
||||
|
# |
||||
|
# 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 LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE |
||||
|
# GENERAL PUBLIC LICENSE (LGPL v3) along with this program. |
||||
|
# If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################### |
||||
|
from openerp import models, fields, api |
||||
|
|
||||
|
|
||||
|
class PurchaseOrder(models.Model): |
||||
|
_inherit = 'purchase.order' |
||||
|
|
||||
|
@api.model |
||||
|
def _prepare_agreement_vals(self, order): |
||||
|
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_line, agreement): |
||||
|
return { |
||||
|
'agreement_id': agreement.id, |
||||
|
'product_id': order_line.product_id.id, |
||||
|
'quantity': order_line.product_qty, |
||||
|
} |
||||
|
|
||||
|
@api.multi |
||||
|
def action_button_generate_agreement(self): |
||||
|
agreements = [] |
||||
|
agreement_obj = self.env['purchase.recurring_orders.agreement'] |
||||
|
agreement_line_obj = self.env['purchase.recurring_orders.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_line in purchase_order.order_line: |
||||
|
agreement_line_vals = self._prepare_agreement_line_vals( |
||||
|
order_line, agreement) |
||||
|
agreement_line_obj.create(agreement_line_vals) |
||||
|
if len(agreements) == 1: |
||||
|
view = self.env.ref( |
||||
|
'purchase_recurring_orders.' |
||||
|
'view_purchase_recurring_orders_agreement_form') |
||||
|
return { |
||||
|
'type': 'ir.actions.act_window', |
||||
|
'res_model': 'purchase.recurring_orders.agreement', |
||||
|
'res_id': agreement[0].id, |
||||
|
'view_type': 'form', |
||||
|
'view_mode': 'form', |
||||
|
'view_id': view.id, |
||||
|
'target': 'current', |
||||
|
'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.') |
||||
|
agreement_id = fields.Many2one( |
||||
|
comodel_name='purchase.recurring_orders.agreement', |
||||
|
string='Agreement reference', ondelete='restrict') |
||||
|
|
||||
|
@api.multi |
||||
|
def view_order(self): |
||||
|
return { |
||||
|
'view_type': 'form', |
||||
|
'view_mode': 'form', |
||||
|
'res_model': 'purchase.order', |
||||
|
'context': self.env.context, |
||||
|
'res_id': self[:1].id, |
||||
|
'view_id': [self.env.ref('purchase.purchase_order_form').id], |
||||
|
'type': 'ir.actions.act_window', |
||||
|
'nodestroy': True |
||||
|
} |
@ -0,0 +1,428 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################## |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# Copyright (C) 2009-TODAY Cybrosys Technologies(<http://www.cybrosys.com>). |
||||
|
# Author: Jesni Banu(<http://www.cybrosys.com>) |
||||
|
# you can modify it under the terms of the GNU LESSER |
||||
|
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. |
||||
|
# |
||||
|
# It is forbidden to publish, distribute, sublicense, or sell copies |
||||
|
# of the Software or modified copies of the Software. |
||||
|
# |
||||
|
# 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 LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE |
||||
|
# GENERAL PUBLIC LICENSE (LGPL v3) along with this program. |
||||
|
# If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################### |
||||
|
from datetime import timedelta |
||||
|
from dateutil.relativedelta import relativedelta |
||||
|
from openerp import models, fields, api, exceptions, _ |
||||
|
import openerp.addons.decimal_precision as dp |
||||
|
|
||||
|
|
||||
|
class Agreement(models.Model): |
||||
|
_name = 'purchase.recurring_orders.agreement' |
||||
|
_inherit = ['mail.thread'] |
||||
|
_description = "Recurring orders agreement" |
||||
|
|
||||
|
@api.model |
||||
|
def __get_next_term_date(self, date, unit, interval): |
||||
|
if unit == 'days': |
||||
|
return date + timedelta(days=interval) |
||||
|
elif unit == 'weeks': |
||||
|
return date + timedelta(weeks=interval) |
||||
|
elif unit == 'months': |
||||
|
return date + relativedelta(months=interval) |
||||
|
elif unit == 'years': |
||||
|
return date + relativedelta(years=interval) |
||||
|
|
||||
|
@api.multi |
||||
|
def _compute_next_expiration_date(self): |
||||
|
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.Date.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.Date.from_string(agreement.last_renovation_date or |
||||
|
agreement.start_date), |
||||
|
agreement.prolong_unit, agreement.prolong_interval) |
||||
|
|
||||
|
def _default_company_id(self): |
||||
|
company_model = self.env['res.company'] |
||||
|
company_id = company_model._company_default_get('purchase') |
||||
|
return company_model.browse(company_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='Unchecking this field, quotas are not generated') |
||||
|
partner_id = fields.Many2one( |
||||
|
comodel_name='res.partner', string='Supplier', index=True, |
||||
|
change_default=True, required=True, |
||||
|
help="Supplier you are making the agreement with") |
||||
|
company_id = fields.Many2one( |
||||
|
comodel_name='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.", required=True) |
||||
|
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 = fields.One2many( |
||||
|
comodel_name='purchase.recurring_orders.agreement.line', |
||||
|
inverse_name='agreement_id', string='Agreement lines') |
||||
|
order_line = fields.One2many( |
||||
|
comodel_name='purchase.order', copy=False, inverse_name='agreement_id', |
||||
|
string='Orders', readonly=True) |
||||
|
renewal_line = fields.One2many( |
||||
|
comodel_name='purchase.recurring_orders.agreement.renewal', copy=False, |
||||
|
inverse_name='agreement_id', string='Renewal lines', readonly=True) |
||||
|
last_renovation_date = fields.Date( |
||||
|
string='Last renovation date', |
||||
|
help="Last date when agreement was renewed (same as start date if not " |
||||
|
"renewed)") |
||||
|
next_expiration_date = fields.Date( |
||||
|
compute="_compute_next_expiration_date", string='Next expiration date') |
||||
|
state = fields.Selection( |
||||
|
selection=[('empty', 'Without orders'), |
||||
|
('first', 'First order created'), |
||||
|
('orders', 'With orders')], |
||||
|
string='State', readonly=True, default='empty') |
||||
|
renewal_state = fields.Selection( |
||||
|
selection=[('not_renewed', 'Agreement not renewed'), |
||||
|
('renewed', 'Agreement renewed')], |
||||
|
string='Renewal state', readonly=True, default='not_renewed') |
||||
|
notes = fields.Text('Notes') |
||||
|
|
||||
|
_sql_constraints = [ |
||||
|
('number_uniq', 'unique(number)', 'Agreement number must be unique !'), |
||||
|
] |
||||
|
|
||||
|
@api.constrains('start_date', 'end_date') |
||||
|
def _check_dates(self): |
||||
|
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): |
||||
|
if not vals.get('start_date'): |
||||
|
vals['start_date'] = fields.Date.today() |
||||
|
if not vals.get('number'): |
||||
|
vals['number'] = self.env['ir.sequence'].get( |
||||
|
'purchase.r_o.agreement.sequence') |
||||
|
return super(Agreement, self).create(vals) |
||||
|
|
||||
|
@api.multi |
||||
|
def write(self, vals): |
||||
|
value = super(Agreement, self).write(vals) |
||||
|
if (any(vals.get(x) is not None for x in |
||||
|
['active', 'number', 'agreement_line', 'prolong', 'end_date', |
||||
|
'prolong_interval', 'prolong_unit', 'partner_id'])): |
||||
|
self.unlink_orders(fields.Date.today()) |
||||
|
return value |
||||
|
|
||||
|
@api.model |
||||
|
def copy(self, id, default=None): |
||||
|
agreement_record = self.browse(id) |
||||
|
default.update({ |
||||
|
'state': 'empty', |
||||
|
'active': True, |
||||
|
'name': '%s*' % agreement_record['name'], |
||||
|
}) |
||||
|
return super(Agreement, self).copy(id, default=default) |
||||
|
|
||||
|
@api.multi |
||||
|
def unlink(self): |
||||
|
for agreement in self: |
||||
|
if any(agreement.mapped('order_line')): |
||||
|
raise exceptions.Warning( |
||||
|
_('You cannot remove agreements with confirmed orders!')) |
||||
|
self.unlink_orders(fields.Date.from_string(fields.Date.today())) |
||||
|
return models.Model.unlink(self) |
||||
|
|
||||
|
@api.multi |
||||
|
def onchange_start_date(self, start_date=False): |
||||
|
if not start_date: |
||||
|
return {} |
||||
|
result = {'value': {'last_renovation_date': start_date}} |
||||
|
return result |
||||
|
|
||||
|
@api.model |
||||
|
def revise_agreements_expirations_planned(self): |
||||
|
for agreement in self.search([('prolong', '=', 'unlimited')]): |
||||
|
if agreement.next_expiration_date <= fields.Date.today(): |
||||
|
agreement.write({'prolong': 'unlimited'}) |
||||
|
return True |
||||
|
|
||||
|
@api.model |
||||
|
def _prepare_purchase_order_vals(self, agreement, date): |
||||
|
order_obj = self.env['purchase.order'].with_context( |
||||
|
company_id=agreement.company_id.id) |
||||
|
order_vals = { |
||||
|
'date_order': date, |
||||
|
'date_confirm': date, |
||||
|
'origin': agreement.number, |
||||
|
'partner_id': agreement.partner_id.id, |
||||
|
'state': 'draft', |
||||
|
'company_id': agreement.company_id.id, |
||||
|
'from_agreement': True, |
||||
|
'agreement_id': agreement.id, |
||||
|
'location_id': 1, |
||||
|
} |
||||
|
order_vals.update(order_obj.onchange_partner_id( |
||||
|
agreement.partner_id.id)['value']) |
||||
|
order_vals['user_id'] = agreement.partner_id.user_id.id |
||||
|
return order_vals |
||||
|
|
||||
|
@api.model |
||||
|
def _prepare_purchase_order_line_vals(self, agreement_line, order): |
||||
|
order_line_obj = self.env['purchase.order.line'].with_context( |
||||
|
company_id=self.company_id.id) |
||||
|
order_line_vals = { |
||||
|
'order_id': order.id, |
||||
|
'product_id': agreement_line.product_id.id, |
||||
|
'product_qty': agreement_line.quantity, |
||||
|
} |
||||
|
order_line_vals.update(order_line_obj.product_id_change( |
||||
|
pricelist_id=order.pricelist_id.id, |
||||
|
product_id= agreement_line.product_id.id, |
||||
|
qty=agreement_line.quantity, |
||||
|
uom_id=agreement_line.uom_id, |
||||
|
partner_id=order.partner_id.id, |
||||
|
fiscal_position_id=order.fiscal_position.id)['value']) |
||||
|
if agreement_line.specific_price: |
||||
|
order_line_vals['price_unit'] = agreement_line.specific_price |
||||
|
order_line_vals['taxes_id'] = [(6, 0, tuple(order_line_vals['taxes_id']))] |
||||
|
if agreement_line.additional_description: |
||||
|
order_line_vals['name'] += " %s" % ( |
||||
|
agreement_line.additional_description) |
||||
|
return order_line_vals |
||||
|
|
||||
|
@api.multi |
||||
|
def create_order(self, date, agreement_lines): |
||||
|
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: |
||||
|
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.Date.today()}) |
||||
|
if self.state != 'orders': |
||||
|
self.state = 'orders' |
||||
|
return order |
||||
|
|
||||
|
@api.multi |
||||
|
def _get_next_order_date(self, line, start_date): |
||||
|
self.ensure_one() |
||||
|
next_date = fields.Date.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 |
||||
|
|
||||
|
@api.multi |
||||
|
def generate_agreement_orders(self, start_date, end_date): |
||||
|
self.ensure_one() |
||||
|
if not self.active: |
||||
|
return |
||||
|
lines_to_order = {} |
||||
|
exp_date = fields.Date.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: |
||||
|
if not line.active_chk: |
||||
|
continue |
||||
|
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[ |
||||
|
'purchase.recurring_orders.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() |
||||
|
dates.sort() |
||||
|
for date in dates: |
||||
|
order = self.order_line.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.Date.to_string(date), lines_to_order[date]) |
||||
|
|
||||
|
@api.multi |
||||
|
def generate_initial_order(self): |
||||
|
self.ensure_one() |
||||
|
agreement_lines = self.mapped('agreement_line').filtered('active_chk') |
||||
|
order = self.create_order(self.start_date, agreement_lines) |
||||
|
self.write({'state': 'first'}) |
||||
|
order.signal_workflow('order_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): |
||||
|
if start_date: |
||||
|
start_date = fields.Date.from_string(start_date) |
||||
|
self.search([]).generate_next_orders( |
||||
|
years=years, start_date=start_date) |
||||
|
|
||||
|
@api.multi |
||||
|
def generate_next_year_orders(self): |
||||
|
return self.generate_next_orders(years=1) |
||||
|
|
||||
|
@api.multi |
||||
|
def generate_next_orders(self, years=1, start_date=None): |
||||
|
if not start_date: |
||||
|
start_date = fields.Date.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): |
||||
|
tomorrow = fields.Date.to_string( |
||||
|
fields.Date.from_string(fields.Date.today()) + timedelta(days=1)) |
||||
|
orders = self.env['purchase.order'].search([ |
||||
|
('agreement_id', '!=', False), |
||||
|
('state', 'in', ('draft', 'sent')), |
||||
|
('date_order', '<', tomorrow) |
||||
|
]) |
||||
|
for order in orders: |
||||
|
order.signal_workflow('order_confirm') |
||||
|
|
||||
|
@api.multi |
||||
|
def unlink_orders(self, start_date): |
||||
|
orders = self.mapped('order_line').filtered( |
||||
|
lambda x: (x.state in ('draft', 'sent') and |
||||
|
x.date_order >= start_date)) |
||||
|
orders.unlink() |
||||
|
|
||||
|
|
||||
|
class AgreementLine(models.Model): |
||||
|
_name = 'purchase.recurring_orders.agreement.line' |
||||
|
|
||||
|
uom_id = fields.Many2one('product_uom', string="Uom") |
||||
|
active_chk = fields.Boolean( |
||||
|
string='Active', default=True, |
||||
|
help='Unchecking this field, this quota is not generated') |
||||
|
agreement_id = fields.Many2one( |
||||
|
comodel_name='purchase.recurring_orders.agreement', |
||||
|
string='Agreement reference', ondelete='cascade') |
||||
|
product_id = fields.Many2one( |
||||
|
comodel_name='product.product', string='Product', ondelete='set null', |
||||
|
required=True) |
||||
|
name = fields.Char( |
||||
|
related="product_id.name", string='Description', store=False) |
||||
|
additional_description = fields.Char( |
||||
|
string='Add. 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) |
||||
|
discount = fields.Float(string='Discount (%)', digits=(16, 2)) |
||||
|
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, default='months') |
||||
|
last_order_date = fields.Date( |
||||
|
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.multi |
||||
|
def onchange_product_id(self, product_id=False): |
||||
|
result = {} |
||||
|
if product_id: |
||||
|
product = self.env['product.product'].browse(product_id) |
||||
|
if product: |
||||
|
result['value'] = {'name': product['name']} |
||||
|
return result |
||||
|
|
||||
|
|
||||
|
class AgreementRenewal(models.Model): |
||||
|
_name = 'purchase.recurring_orders.agreement.renewal' |
||||
|
|
||||
|
agreement_id = fields.Many2one( |
||||
|
comodel_name='purchase.recurring_orders.agreement', |
||||
|
string='Agreement reference', ondelete='cascade', select=True) |
||||
|
date = fields.Date(string='Date', help="Date of the renewal") |
||||
|
comments = fields.Char( |
||||
|
string='Comments', size=200, help='Renewal comments') |
|
After Width: | Height: | Size: 124 KiB |
After Width: | Height: | Size: 124 KiB |
After Width: | Height: | Size: 127 KiB |
After Width: | Height: | Size: 119 KiB |
After Width: | Height: | Size: 127 KiB |
After Width: | Height: | Size: 50 KiB |
After Width: | Height: | Size: 8.9 KiB |
@ -0,0 +1,79 @@ |
|||||
|
<section class="oe_container"> |
||||
|
<div class="oe_row oe_spaced"> |
||||
|
<h2 class="oe_slogan">Purchase Recurring Order</h2> |
||||
|
<h3 class="oe_slogan">This module allows you to create recurring orders for Purchases</h3> |
||||
|
<h4 class="oe_slogan">Cybrosys Techno Solutions , www.cybrosys.com</h4> |
||||
|
</div> |
||||
|
<div class="oe_row oe_spaced"> |
||||
|
<div> |
||||
|
☛This module is useful for create recurring orders for Purchases. |
||||
|
Based on information provided it will create purchase orders recurrently. |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="oe_row oe_spaced"> |
||||
|
<div class="oe_span12"> |
||||
|
<p class='oe_mt32'> |
||||
|
☛ Go to Purchases -> Purchase -> Recurring order agreements. <br/> |
||||
|
Here you can fill all the information related to Recurring order agreements. Once saved, you can generate initial purchase order. |
||||
|
</p> |
||||
|
</div> |
||||
|
<div class="oe_span12"> |
||||
|
<div class="oe_demo oe_picture oe_screenshot"> |
||||
|
<img src="81.png"> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</section> |
||||
|
|
||||
|
<section class="oe_container oe_dark"> |
||||
|
<div class="oe_row oe_spaced"> |
||||
|
<div class="oe_span12"> |
||||
|
<p class='oe_mt32'> |
||||
|
☛ Here you can generate initial purchase order through "Generate initial Order" button.</p> |
||||
|
</div> |
||||
|
<div class="oe_span6"> |
||||
|
<div class="oe_demo oe_picture oe_screenshot"> |
||||
|
<img src="81.1.png"> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="oe_span6"> |
||||
|
<div class="oe_demo oe_picture oe_screenshot"> |
||||
|
<img src="82.png"> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</section> |
||||
|
|
||||
|
<section class="oe_container"> |
||||
|
<div class="oe_row oe_spaced"> |
||||
|
<div class="oe_span12"> |
||||
|
<p class='oe_mt32'> |
||||
|
☛ You can also create recurring orders from purchase order through "Generate agreement" button. |
||||
|
</p> |
||||
|
</div> |
||||
|
<div class="oe_span12"> |
||||
|
<div class="oe_demo oe_picture oe_screenshot"> |
||||
|
<img src="83.png"> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</section> |
||||
|
|
||||
|
<section class="oe_container oe_dark"> |
||||
|
<h2 class="oe_slogan" style="margin-top:20px;" >Need Any Help.?</h2> |
||||
|
<div class="oe_slogan" style="margin-top:10px !important;"> |
||||
|
<a class="btn btn-primary btn-lg mt8" |
||||
|
style="color: #FFFFFF !important;" href="http://www.cybrosys.com"><i |
||||
|
class="fa fa-envelope"></i> Email </a> <a |
||||
|
class="btn btn-primary btn-lg mt8" style="color: #FFFFFF !important;" |
||||
|
href="http://www.cybrosys.com/contact/"><i |
||||
|
class="fa fa-phone"></i> Contact Us </a> <a |
||||
|
class="btn btn-primary btn-lg mt8" style="color: #FFFFFF !important;" |
||||
|
href="http://www.cybrosys.com/odoo-customization-and-installation/"><i |
||||
|
class="fa fa-check-square"></i> Request Customization </a> |
||||
|
</div> |
||||
|
<img src="cybro_logo.png" style="width: 190px; margin-bottom: 20px;" class="center-block"> |
||||
|
</section> |
||||
|
|
||||
|
|
||||
|
|
@ -0,0 +1,2 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
from . import test_recurring_orders |
@ -0,0 +1,32 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
from openerp.tests import common |
||||
|
|
||||
|
|
||||
|
class TestRecurringOrder(common.TransactionCase): |
||||
|
def setUp(self): |
||||
|
super(TestRecurringOrder, self).setUp() |
||||
|
self.agreement_model = self.env['purchase.recurring_orders.agreement'] |
||||
|
self.agreement = self.agreement_model.create( |
||||
|
{'name': 'Agreement test', |
||||
|
'partner_id': self.env.ref('base.res_partner_1').id}) |
||||
|
self.line_model = self.env['purchase.recurring_orders.agreement.line'] |
||||
|
self.agreement_line = self.line_model.create( |
||||
|
{'agreement_id': self.agreement.id, |
||||
|
'product_id': self.env.ref('product.product_product_1').id}) |
||||
|
self.agreement.generate_next_year_orders() |
||||
|
|
||||
|
def test_order_creation_next_year(self): |
||||
|
self.assertEqual(len(self.agreement.order_line), 12) |
||||
|
|
||||
|
def test_order_creation_two_years(self): |
||||
|
self.agreement.generate_next_orders(years=2) |
||||
|
self.assertEqual(len(self.agreement.order_line), 24) |
||||
|
|
||||
|
def test_order_cleanup_change(self): |
||||
|
self.agreement.active = False |
||||
|
self.assertEqual(len(self.agreement.order_line), 0) |
||||
|
|
||||
|
def test_order_cleanup_change_with_confirmed_and_order_line(self): |
||||
|
self.agreement.order_line[0].action_button_confirm() |
||||
|
self.agreement.prolong_interval = 2 |
||||
|
self.assertEqual(len(self.agreement.order_line), 1) |
@ -0,0 +1,28 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<openerp> |
||||
|
<data> |
||||
|
<record id="view_purchase_order_form" model="ir.ui.view"> |
||||
|
<field name="name">purchase.order.form.recurring_orders_inherited</field> |
||||
|
<field name="model">purchase.order</field> |
||||
|
<field name="inherit_id" ref="purchase.purchase_order_form"/> |
||||
|
<field name="arch" type="xml"> |
||||
|
<button name="action_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.form.list.select_inherited</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> |
||||
|
|
||||
|
</data> |
||||
|
</openerp> |
@ -0,0 +1,170 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<openerp> |
||||
|
<data> |
||||
|
<record model="ir.ui.view" id="view_purchase_recurring_orders_agreement_renewal_tree"> |
||||
|
<field name="name">purchase.recurring_orders.agreement.renewal.tree</field> |
||||
|
<field name="model">purchase.recurring_orders.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> |
||||
|
|
||||
|
<record model="ir.ui.view" id="view_purchase_recurring_orders_agreement_tree"> |
||||
|
<field name="name">purchase.recurring_orders.agreement.tree</field> |
||||
|
<field name="model">purchase.recurring_orders.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> |
||||
|
|
||||
|
<record model="ir.ui.view" id="view_purchase_recurring_orders_agreement_form"> |
||||
|
<field name="name">purchase.recurring_orders.agreement.form</field> |
||||
|
<field name="model">purchase.recurring_orders.agreement</field> |
||||
|
<field name="type">form</field> |
||||
|
<field name="priority" eval="6"/> |
||||
|
<field name="arch" type="xml"> |
||||
|
<form string="Agreement" version="7.0"> |
||||
|
<header> |
||||
|
<button name="generate_initial_order" string="Generate initial order" class="oe_highlight" type="object" states="empty"/> |
||||
|
<button name="generate_next_year_orders" string="Generate next year orders" type="object" icon="gtk-execute"/> |
||||
|
<button name="%(action_purchase_recurring_orders_renew_wizard)d" type="action" string="Renew agreement" attrs="{'invisible': [('prolong','!=', 'recurrent')]}"/> |
||||
|
</header> |
||||
|
<sheet> |
||||
|
<group> |
||||
|
<group> |
||||
|
<field name="name"/> |
||||
|
<field name="partner_id" domain="[('customer','=',True)]"/> |
||||
|
<field name="company_id" groups="base.group_multi_company"/> |
||||
|
</group> |
||||
|
<group> |
||||
|
<field name="active"/> |
||||
|
<field name="number"/> |
||||
|
<field name="start_date" on_change="onchange_start_date(start_date)" attrs="{'readonly':[('state','!=','empty')]}"/> |
||||
|
</group> |
||||
|
</group> |
||||
|
<group> |
||||
|
<group> |
||||
|
<field name="prolong" attrs="{'readonly':[('renewal_state','!=','not_renewed')]}"/> |
||||
|
<field name="next_expiration_date"/> |
||||
|
<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')]}"/> |
||||
|
<field name="prolong_unit" 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> |
||||
|
<notebook colspan="4"> |
||||
|
<page string="Lines"> |
||||
|
<field name="agreement_line"> |
||||
|
<tree string="Agreement lines" editable="bottom"> |
||||
|
<field name="active_chk"/> |
||||
|
<field name="product_id" on_change="onchange_product_id(product_id)"/> |
||||
|
<field name="additional_description"/> |
||||
|
<field name="quantity"/> |
||||
|
<field name="uom_id"/> |
||||
|
<field name="list_price"/> |
||||
|
<field name="specific_price"/> |
||||
|
<field name="discount"/> |
||||
|
<field name="ordering_interval"/> |
||||
|
<field name="ordering_unit"/> |
||||
|
<field name="last_order_date" readonly="True"/> |
||||
|
</tree> |
||||
|
</field> |
||||
|
</page> |
||||
|
<page string="Orders"> |
||||
|
<field colspan="4" mode="tree" name="order_line" widget="one2many_list" nolabel="1"> |
||||
|
<tree editable="bottom"> |
||||
|
<button name="view_order" |
||||
|
string="View order" |
||||
|
type="object" |
||||
|
icon="terp-gtk-go-back-rtl"/> |
||||
|
<field name="name"/> |
||||
|
<field name="date_order"/> |
||||
|
<field name="state"/> |
||||
|
</tree> |
||||
|
</field> |
||||
|
</page> |
||||
|
<page string="Renewals" attrs="{'invisible': [('prolong','!=', 'recurrent')]}"> |
||||
|
<field colspan="4" mode="tree" name="renewal_line" 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> |
||||
|
|
||||
|
<record id="view_purchases_ro_agreement_filter" model="ir.ui.view"> |
||||
|
<field name="name">purchase.recurring_orders.agreement.select</field> |
||||
|
<field name="model">purchase.recurring_orders.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> |
||||
|
|
||||
|
<record model="ir.actions.act_window" id="action_purchase_recurring_orders_agreement"> |
||||
|
<field name="name">Recurring orders agreements</field> |
||||
|
<field name="res_model">purchase.recurring_orders.agreement</field> |
||||
|
<field name="view_type">form</field> |
||||
|
<field name="view_mode">tree,form</field> |
||||
|
<field name="search_view_id" ref="view_purchases_ro_agreement_filter"/> |
||||
|
<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 that requires a recurring order. |
||||
|
</p> |
||||
|
</field> |
||||
|
</record> |
||||
|
|
||||
|
<record model="ir.actions.act_window.view" id="action_purchase_recurring_orders_agreement_view_tree"> |
||||
|
<field name="sequence" eval="1"/> |
||||
|
<field name="view_mode">tree</field> |
||||
|
<field name="view_id" ref="view_purchase_recurring_orders_agreement_tree"/> |
||||
|
<field name="act_window_id" ref="action_purchase_recurring_orders_agreement"/> |
||||
|
</record> |
||||
|
|
||||
|
<record model="ir.actions.act_window.view" id="action_purchase_recurring_orders_agreement_view_form"> |
||||
|
<field name="sequence" eval="2"/> |
||||
|
<field name="view_mode">form</field> |
||||
|
<field name="view_id" ref="view_purchase_recurring_orders_agreement_form"/> |
||||
|
<field name="act_window_id" ref="action_purchase_recurring_orders_agreement"/> |
||||
|
</record> |
||||
|
|
||||
|
<menuitem name="Recurring orders agreements" id="menu_recurring_orders_agreements" |
||||
|
parent="purchase.menu_procurement_management" action="action_purchase_recurring_orders_agreement" |
||||
|
sequence="4"/> |
||||
|
|
||||
|
</data> |
||||
|
</openerp> |
@ -0,0 +1,33 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<openerp> |
||||
|
<data> |
||||
|
|
||||
|
<record id="act_res_partner_2_agreement" model="ir.actions.act_window"> |
||||
|
<field name="name">Agreements</field> |
||||
|
<field name="res_model">purchase.recurring_orders.agreement</field> |
||||
|
<field name="view_type">form</field> |
||||
|
<field name="view_mode">tree,form</field> |
||||
|
<field name="context">{'search_default_partner_id': active_id}</field> |
||||
|
<field name="groups_id" eval="[(4, ref('base.group_sale_salesman'))]"/> |
||||
|
</record> |
||||
|
|
||||
|
<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='buttons']" position="inside"> |
||||
|
<button name="%(act_res_partner_2_agreement)d" |
||||
|
type="action" |
||||
|
class="oe_stat_button" |
||||
|
icon="fa-file-text-o" |
||||
|
string="Agreements" |
||||
|
attrs="{'invisible': [('customer', '=', False)]}" |
||||
|
groups="base.group_sale_salesman"/> |
||||
|
</xpath> |
||||
|
</field> |
||||
|
</record> |
||||
|
|
||||
|
</data> |
||||
|
</openerp> |
@ -0,0 +1,23 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################## |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# Copyright (C) 2009-TODAY Cybrosys Technologies(<http://www.cybrosys.com>). |
||||
|
# Author: Jesni Banu(<http://www.cybrosys.com>) |
||||
|
# you can modify it under the terms of the GNU LESSER |
||||
|
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. |
||||
|
# |
||||
|
# It is forbidden to publish, distribute, sublicense, or sell copies |
||||
|
# of the Software or modified copies of the Software. |
||||
|
# |
||||
|
# 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 LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE |
||||
|
# GENERAL PUBLIC LICENSE (LGPL v3) along with this program. |
||||
|
# If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################### |
||||
|
from . import renew_wizard |
@ -0,0 +1,55 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################## |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# Copyright (C) 2009-TODAY Cybrosys Technologies(<http://www.cybrosys.com>). |
||||
|
# Author: Jesni Banu(<http://www.cybrosys.com>) |
||||
|
# you can modify it under the terms of the GNU LESSER |
||||
|
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. |
||||
|
# |
||||
|
# It is forbidden to publish, distribute, sublicense, or sell copies |
||||
|
# of the Software or modified copies of the Software. |
||||
|
# |
||||
|
# 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 LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE |
||||
|
# GENERAL PUBLIC LICENSE (LGPL v3) along with this program. |
||||
|
# If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################### |
||||
|
from openerp import models, fields, api |
||||
|
|
||||
|
|
||||
|
class RenewWizard(models.TransientModel): |
||||
|
_name = "purchase.recurring_orders.renew_wizard" |
||||
|
|
||||
|
def _get_renewal_date(self): |
||||
|
agreements = self.env['purchase.recurring_orders.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') |
||||
|
|
||||
|
@api.multi |
||||
|
def create_renewal(self, cr, uid, ids, context=None): |
||||
|
self.ensure_one() |
||||
|
agreement_ids = context.get('active_ids', []) |
||||
|
for agreement_id in agreement_ids: |
||||
|
self.env['purchase.recurring_orders.agreement.renewal'].create( |
||||
|
{'agreement_id': agreement_id, |
||||
|
'date': self.date, |
||||
|
'comments': self.comments}) |
||||
|
agreement_model = self.env['purchase.recurring_orders.agreement'] |
||||
|
agreement_model.browse(agreement_ids).write( |
||||
|
{'last_renovation_date': self.date, |
||||
|
'renewal_state': 'renewed'}) |
||||
|
return True |
@ -0,0 +1,33 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<openerp> |
||||
|
<data> |
||||
|
|
||||
|
<record id="view_purchase_recurring_orders_renew_wizard" model="ir.ui.view"> |
||||
|
<field name="name">Renew agreement wizard</field> |
||||
|
<field name="model">purchase.recurring_orders.renew_wizard</field> |
||||
|
<field name="type">form</field> |
||||
|
<field name="arch" type="xml"> |
||||
|
<form string="Renew agreement wizard"> |
||||
|
<label string="Set renewal agreement data" colspan="4"/> |
||||
|
<newline/> |
||||
|
<field name="date"/> |
||||
|
<newline/> |
||||
|
<field name="comments" default_focus="1"/> |
||||
|
<separator string="" colspan="4" /> |
||||
|
<button special="cancel" string="Cancel" icon="gtk-cancel"/> |
||||
|
<button name="create_renewal" string="Renew agreement" type="object" icon="gtk-ok"/> |
||||
|
</form> |
||||
|
</field> |
||||
|
</record> |
||||
|
|
||||
|
<act_window name="Renew agreement wizard" |
||||
|
res_model="purchase.recurring_orders.renew_wizard" |
||||
|
src_model="purchase.recurring_orders.agreement" |
||||
|
view_mode="form" |
||||
|
target="new" |
||||
|
multi="True" |
||||
|
key2="client_action_multi" |
||||
|
id="action_purchase_recurring_orders_renew_wizard"/> |
||||
|
|
||||
|
</data> |
||||
|
</openerp> |