Merge branch 'master-recurring-fatemi' into 'master'
[ADD]:Added recurring,recurring_sale,recurring_purchase,recurring_account See merge request flectra-hq/flectra!125
5
addons/recurring/__init__.py
Normal file
@ -0,0 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo, Flectra. See LICENSE file for full copyright and licensing
|
||||
# details.
|
||||
|
||||
from . import models
|
28
addons/recurring/__manifest__.py
Normal file
@ -0,0 +1,28 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo, Flectra. See LICENSE file for full copyright and licensing
|
||||
# details.
|
||||
|
||||
{
|
||||
'name': 'Recurring Documents',
|
||||
'category': 'Extra Tools',
|
||||
'description': """
|
||||
Create recurring documents.
|
||||
===========================
|
||||
|
||||
This module allows to create new documents and add recurring on that
|
||||
document.
|
||||
|
||||
e.g. To have an invoice generated automatically periodically:
|
||||
-------------------------------------------------------------
|
||||
* Define a document type based on Invoice object
|
||||
* Define a recurring whose source document is the document defined as
|
||||
above. Specify the interval information and partner to be invoiced.
|
||||
Module taken from odoov10 subscription.
|
||||
""",
|
||||
'depends': ['base'],
|
||||
'data': [
|
||||
'security/ir.model.access.csv',
|
||||
'views/recurring_view.xml',
|
||||
],
|
||||
'demo': ['demo/recurring_demo.xml'],
|
||||
}
|
27
addons/recurring/demo/recurring_demo.xml
Normal file
@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" ?>
|
||||
<flectra noupdate="1">
|
||||
|
||||
<record id="recurring_document_partner0" model="recurring.document">
|
||||
<field eval="1" name="active"/>
|
||||
<field name="model" ref="base.model_res_partner"/>
|
||||
<field name="name">Partner</field>
|
||||
</record>
|
||||
<record id="recurring_document_fields_2" model="recurring.document.fields">
|
||||
<field name="field" ref="base.field_res_partner_name"/>
|
||||
<field name="value">false</field>
|
||||
<field model="recurring.document" name="document_id" search="[('name', '=', u'Partner')]"/>
|
||||
</record>
|
||||
<record id="recurring_document_fields_3" model="recurring.document.fields">
|
||||
<field name="field" ref="base.field_res_partner_child_ids"/>
|
||||
<field model="recurring.document" name="document_id" search="[('name', '=', u'Partner')]"/>
|
||||
</record>
|
||||
|
||||
<record id="recurring_partner0" model="recurring">
|
||||
<field name="name">Partner Recurring</field>
|
||||
<field name="exec_init">5</field>
|
||||
<field name="interval_type">minutes</field>
|
||||
<field name="date_init" eval="datetime.now()"/>
|
||||
<field name="doc_source" ref='base.res_partner_2'/>
|
||||
</record>
|
||||
|
||||
</flectra>
|
392
addons/recurring/i18n/recurring.pot
Normal file
@ -0,0 +1,392 @@
|
||||
# Translation of Flectra Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * recurring
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Flectra Server 1.3\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-08-01 17:11+0000\n"
|
||||
"PO-Revision-Date: 2018-08-01 17:11+0000\n"
|
||||
"Last-Translator: <>\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"Plural-Forms: \n"
|
||||
|
||||
#. module: recurring
|
||||
#: model:ir.model.fields,field_description:recurring.field_recurring_active
|
||||
#: model:ir.model.fields,field_description:recurring.field_recurring_document_active
|
||||
msgid "Active"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: model:ir.ui.view,arch_db:recurring.view_recurring_wizard_form
|
||||
msgid "Cancel"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: model:ir.model.fields,field_description:recurring.field_recurring_create_uid
|
||||
#: model:ir.model.fields,field_description:recurring.field_recurring_document_create_uid
|
||||
#: model:ir.model.fields,field_description:recurring.field_recurring_document_fields_create_uid
|
||||
#: model:ir.model.fields,field_description:recurring.field_recurring_history_create_uid
|
||||
msgid "Created by"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: model:ir.model.fields,field_description:recurring.field_recurring_create_date
|
||||
#: model:ir.model.fields,field_description:recurring.field_recurring_document_create_date
|
||||
#: model:ir.model.fields,field_description:recurring.field_recurring_document_fields_create_date
|
||||
#: model:ir.model.fields,field_description:recurring.field_recurring_history_create_date
|
||||
msgid "Created on"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: model:ir.model.fields,field_description:recurring.field_recurring_cron_id
|
||||
msgid "Cron Job"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: code:addons/recurring/tests/test_recurring.py:21
|
||||
#, python-format
|
||||
msgid "Cron is not created"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: selection:recurring.document.fields,value:0
|
||||
msgid "Current Date"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: model:ir.model.fields,field_description:recurring.field_recurring_history_date
|
||||
msgid "Date"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: selection:recurring,interval_type:0
|
||||
msgid "Days"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: model:ir.model.fields,field_description:recurring.field_recurring_document_fields_value
|
||||
msgid "Default Value"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: model:ir.model.fields,help:recurring.field_recurring_document_fields_value
|
||||
msgid "Default value is considered for field when new document is generated."
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: model:ir.model.fields,help:recurring.field_recurring_note
|
||||
msgid "Description or Summary of Recurring"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: model:ir.model.fields,field_description:recurring.field_recurring_display_name
|
||||
#: model:ir.model.fields,field_description:recurring.field_recurring_document_display_name
|
||||
#: model:ir.model.fields,field_description:recurring.field_recurring_document_fields_display_name
|
||||
#: model:ir.model.fields,field_description:recurring.field_recurring_history_display_name
|
||||
msgid "Display Name"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: model:ir.model.fields,field_description:recurring.field_recurring_doc_lines
|
||||
#: model:ir.ui.view,arch_db:recurring.view_recurring_form
|
||||
msgid "Documents created"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: selection:recurring,state:0
|
||||
msgid "Done"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: selection:recurring,state:0
|
||||
msgid "Draft"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: selection:recurring.document.fields,value:0
|
||||
msgid "False"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: model:ir.model.fields,field_description:recurring.field_recurring_document_fields_field
|
||||
msgid "Field"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: model:ir.model.fields,field_description:recurring.field_recurring_document_field_ids
|
||||
msgid "Fields"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: model:ir.model.fields,field_description:recurring.field_recurring_date_init
|
||||
msgid "First Date"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: model:ir.ui.view,arch_db:recurring.view_recurring_filter
|
||||
msgid "Group By"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: selection:recurring,interval_type:0
|
||||
msgid "Hours"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: model:ir.model.fields,field_description:recurring.field_recurring_document_fields_id
|
||||
#: model:ir.model.fields,field_description:recurring.field_recurring_document_id
|
||||
#: model:ir.model.fields,field_description:recurring.field_recurring_history_id
|
||||
#: model:ir.model.fields,field_description:recurring.field_recurring_id
|
||||
msgid "ID"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: model:ir.ui.view,arch_db:recurring.view_recurring_wizard_form
|
||||
msgid "IR Recurring"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: model:ir.model.fields,help:recurring.field_recurring_document_active
|
||||
msgid "If the active field is set to False, it will allow you to hide the recurring document without removing it."
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: model:ir.model.fields,help:recurring.field_recurring_active
|
||||
msgid "If the active field is set to False, it will allow you to hide the recurring without removing it."
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: model:ir.model.fields,field_description:recurring.field_recurring_notes
|
||||
#: model:ir.ui.view,arch_db:recurring.view_recurring_form
|
||||
msgid "Internal Notes"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: model:ir.model.fields,field_description:recurring.field_recurring_interval_number
|
||||
msgid "Internal Qty"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: model:ir.model.fields,field_description:recurring.field_recurring_interval_type
|
||||
msgid "Interval Unit"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: model:ir.model.fields,field_description:recurring.field_recurring___last_update
|
||||
#: model:ir.model.fields,field_description:recurring.field_recurring_document___last_update
|
||||
#: model:ir.model.fields,field_description:recurring.field_recurring_document_fields___last_update
|
||||
#: model:ir.model.fields,field_description:recurring.field_recurring_history___last_update
|
||||
msgid "Last Modified on"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: model:ir.model.fields,field_description:recurring.field_recurring_document_fields_write_uid
|
||||
#: model:ir.model.fields,field_description:recurring.field_recurring_document_write_uid
|
||||
#: model:ir.model.fields,field_description:recurring.field_recurring_history_write_uid
|
||||
#: model:ir.model.fields,field_description:recurring.field_recurring_write_uid
|
||||
msgid "Last Updated by"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: model:ir.model.fields,field_description:recurring.field_recurring_document_fields_write_date
|
||||
#: model:ir.model.fields,field_description:recurring.field_recurring_document_write_date
|
||||
#: model:ir.model.fields,field_description:recurring.field_recurring_history_write_date
|
||||
#: model:ir.model.fields,field_description:recurring.field_recurring_write_date
|
||||
msgid "Last Updated on"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: selection:recurring,interval_type:0
|
||||
msgid "Minutes"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: selection:recurring,interval_type:0
|
||||
msgid "Months"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: model:ir.model.fields,field_description:recurring.field_recurring_document_name
|
||||
#: model:ir.model.fields,field_description:recurring.field_recurring_name
|
||||
msgid "Name"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: model:ir.model.fields,field_description:recurring.field_recurring_note
|
||||
#: model:ir.ui.view,arch_db:recurring.view_recurring_form
|
||||
msgid "Notes"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: model:ir.model.fields,field_description:recurring.field_recurring_exec_init
|
||||
msgid "Number of Documents"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: model:ir.model.fields,field_description:recurring.field_recurring_document_model
|
||||
msgid "Object"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: model:ir.model.fields,field_description:recurring.field_recurring_partner_id
|
||||
#: model:ir.ui.view,arch_db:recurring.view_recurring_filter
|
||||
msgid "Partner"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: code:addons/recurring/models/recurring.py:221
|
||||
#, python-format
|
||||
msgid "Please provide another source document.\n"
|
||||
"This one does not exist!"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: model:ir.ui.view,arch_db:recurring.view_recurring_form
|
||||
msgid "Process"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: model:ir.model,name:recurring.model_recurring
|
||||
#: model:ir.model.fields,field_description:recurring.field_recurring_history_recurring_id
|
||||
#: model:ir.ui.view,arch_db:recurring.view_recurring_filter
|
||||
msgid "Recurring"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: model:ir.ui.view,arch_db:recurring.view_recurring_form
|
||||
msgid "Recurring Data"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: model:ir.model,name:recurring.model_recurring_document
|
||||
#: model:ir.model.fields,field_description:recurring.field_recurring_document_fields_document_id
|
||||
#: model:ir.ui.view,arch_db:recurring.document_form
|
||||
#: model:ir.ui.view,arch_db:recurring.document_tree
|
||||
#: model:ir.ui.view,arch_db:recurring.view_recurring_document_filter
|
||||
msgid "Recurring Document"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: model:ir.model,name:recurring.model_recurring_document_fields
|
||||
#: model:ir.ui.view,arch_db:recurring.document_fields_form
|
||||
#: model:ir.ui.view,arch_db:recurring.document_fields_tree
|
||||
msgid "Recurring Document Fields"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: model:ir.actions.act_window,name:recurring.action_recurring_form
|
||||
#: model:ir.actions.act_window,name:recurring.action_recurring_wizard
|
||||
#: model:ir.ui.menu,name:recurring.menu_action_recurring_form
|
||||
msgid "Recurring Documents"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: model:ir.ui.view,arch_db:recurring.recurring_history_form
|
||||
#: model:ir.ui.view,arch_db:recurring.recurring_history_tree
|
||||
msgid "Recurring History"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: model:ir.actions.act_window,name:recurring.action_document_form
|
||||
#: model:ir.ui.menu,name:recurring.menu_action_document_form
|
||||
msgid "Recurring Types"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: model:ir.model,name:recurring.model_recurring_history
|
||||
msgid "Recurring history"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: code:addons/recurring/tests/test_recurring.py:31
|
||||
#, python-format
|
||||
msgid "Recurring is in running state"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: code:addons/recurring/tests/test_recurring.py:34
|
||||
#, python-format
|
||||
msgid "Recurring is not in draft state"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: model:ir.ui.view,arch_db:recurring.view_recurring_form
|
||||
#: model:ir.ui.view,arch_db:recurring.view_recurring_tree
|
||||
msgid "Recurrings"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: model:ir.ui.view,arch_db:recurring.view_recurring_filter
|
||||
#: selection:recurring,state:0
|
||||
msgid "Running"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: model:ir.ui.view,arch_db:recurring.view_recurring_wizard_form
|
||||
msgid "Save"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: model:ir.model.fields,help:recurring.field_recurring_cron_id
|
||||
msgid "Scheduler which runs on recurring"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: model:ir.ui.view,arch_db:recurring.view_recurring_filter
|
||||
msgid "Search Recurring"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: model:ir.ui.view,arch_db:recurring.view_recurring_form
|
||||
msgid "Set to Draft"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: model:ir.model.fields,field_description:recurring.field_recurring_doc_source
|
||||
#: model:ir.model.fields,field_description:recurring.field_recurring_history_document_id
|
||||
msgid "Source Document"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: model:ir.ui.view,arch_db:recurring.view_recurring_wizard_form
|
||||
msgid "Start Recurring"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: model:ir.model.fields,field_description:recurring.field_recurring_state
|
||||
#: model:ir.ui.view,arch_db:recurring.view_recurring_filter
|
||||
msgid "Status"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: model:ir.ui.view,arch_db:recurring.view_recurring_form
|
||||
msgid "Stop"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: model:ir.model.fields,field_description:recurring.field_recurring_user_id
|
||||
#: model:ir.ui.view,arch_db:recurring.view_recurring_filter
|
||||
msgid "User"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: model:ir.model.fields,help:recurring.field_recurring_doc_source
|
||||
msgid "User can choose the source document on which he wants to create documents"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: selection:recurring,interval_type:0
|
||||
msgid "Weeks"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring
|
||||
#: code:addons/recurring/models/recurring.py:247
|
||||
#, python-format
|
||||
msgid "You cannot delete an active recurring!"
|
||||
msgstr ""
|
||||
|
5
addons/recurring/models/__init__.py
Normal file
@ -0,0 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo, Flectra. See LICENSE file for full copyright and licensing
|
||||
# details.
|
||||
|
||||
from . import recurring
|
291
addons/recurring/models/recurring.py
Normal file
@ -0,0 +1,291 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo, Flectra. See LICENSE file for full copyright and licensing
|
||||
# details.
|
||||
|
||||
# TODO:
|
||||
# Error treatment: exception, request, ... -> send request to user_id
|
||||
|
||||
from flectra import api, fields, models, _
|
||||
from flectra.exceptions import UserError, ValidationError
|
||||
|
||||
|
||||
def _get_document_types(self):
|
||||
return [(doc.model.model, doc.name) for doc in self.env[
|
||||
'recurring.document'].search([], order='name')]
|
||||
|
||||
|
||||
class RecurringDocument(models.Model):
|
||||
_name = "recurring.document"
|
||||
_description = "Recurring Document"
|
||||
|
||||
name = fields.Char(string='Name')
|
||||
active = fields.Boolean(
|
||||
help="If the active field is set to False, it will allow you to hide "
|
||||
"the recurring document without removing it.", default=True)
|
||||
model = fields.Many2one('ir.model', string="Object")
|
||||
field_ids = fields.One2many('recurring.document.fields',
|
||||
'document_id', string='Fields', copy=True)
|
||||
|
||||
|
||||
class RecurringDocumentFields(models.Model):
|
||||
_name = "recurring.document.fields"
|
||||
_description = "Recurring Document Fields"
|
||||
_rec_name = 'field'
|
||||
|
||||
field = fields.Many2one('ir.model.fields', domain="[('model_id', '=', "
|
||||
"parent.model)]")
|
||||
value = fields.Selection(
|
||||
[('false', 'False'), ('date', 'Current Date')], string='Default Value',
|
||||
help="Default value is considered for field when new document is "
|
||||
"generated.")
|
||||
document_id = fields.Many2one('recurring.document',
|
||||
string='Recurring Document',
|
||||
ondelete='cascade')
|
||||
|
||||
|
||||
class Recurring(models.Model):
|
||||
_name = "recurring"
|
||||
_description = "Recurring"
|
||||
|
||||
@api.model
|
||||
def default_get(self, fields):
|
||||
res = super(Recurring, self).default_get(fields)
|
||||
active_model = self._context.get('active_model')
|
||||
active_id = self._context.get('active_id')
|
||||
if active_model and active_id:
|
||||
record = self.env[active_model].browse(active_id)
|
||||
if 'partner_id' in self.env[active_model]._fields:
|
||||
res['partner_id'] = record.partner_id.id
|
||||
else:
|
||||
res['name'] = record.name
|
||||
if not res['name']:
|
||||
res['name'] = record.number
|
||||
return res
|
||||
|
||||
@api.onchange('partner_id')
|
||||
def _onchange_partner_id(self):
|
||||
active_model = self._context.get('active_model')
|
||||
active_id = self._context.get('active_id')
|
||||
if self.partner_id and active_model and active_id:
|
||||
record = self.env[active_model].browse(active_id)
|
||||
name = record.name
|
||||
if not name:
|
||||
name = record.number
|
||||
if name:
|
||||
self.name = name + '-' + self.partner_id.name
|
||||
else:
|
||||
self.name = self.partner_id.name
|
||||
|
||||
@api.constrains('partner_id', 'doc_source')
|
||||
def _check_partner_id_doc_source(self):
|
||||
for record in self:
|
||||
if record.partner_id and record.doc_source and 'partner_id' in \
|
||||
self.env[record.doc_source._name]._fields and \
|
||||
record.doc_source.partner_id != record.partner_id:
|
||||
raise ValidationError(_(
|
||||
'Error! Source Document should be related to partner %s' %
|
||||
record.partner_id.name))
|
||||
|
||||
name = fields.Char(string='Name')
|
||||
active = fields.Boolean(
|
||||
help="If the active field is set to False, it will allow you to hide "
|
||||
"the recurring without removing it.", default=True)
|
||||
partner_id = fields.Many2one('res.partner', string='Partner')
|
||||
notes = fields.Text(string='Internal Notes')
|
||||
user_id = fields.Many2one('res.users', string='User',
|
||||
default=lambda self: self.env.user)
|
||||
interval_number = fields.Integer(string='Internal Qty', default=1)
|
||||
interval_type = fields.Selection(
|
||||
[('minutes', 'Minutes'), ('hours', 'Hours'), ('days', 'Days'),
|
||||
('weeks', 'Weeks'), ('months', 'Months')], string='Interval Unit',
|
||||
default='months')
|
||||
exec_init = fields.Integer(string='Number of Documents')
|
||||
date_init = fields.Datetime(string='First Date',
|
||||
default=fields.Datetime.now)
|
||||
state = fields.Selection(
|
||||
[('draft', 'Draft'), ('running', 'Running'), ('done', 'Done')],
|
||||
string='Status', copy=False, default='draft')
|
||||
doc_source = fields.Reference(
|
||||
selection=_get_document_types, string='Source Document',
|
||||
help="User can choose the source document on which he wants to "
|
||||
"create documents")
|
||||
doc_lines = fields.One2many('recurring.history',
|
||||
'recurring_id', string='Documents created')
|
||||
cron_id = fields.Many2one('ir.cron', string='Cron Job',
|
||||
help="Scheduler which runs on recurring",
|
||||
states={'running': [('readonly', True)],
|
||||
'done': [('readonly', True)]})
|
||||
note = fields.Text(string='Notes',
|
||||
help="Description or Summary of Recurring")
|
||||
|
||||
@api.model
|
||||
def _auto_end(self):
|
||||
super(Recurring, self)._auto_end()
|
||||
# drop the FK from recurring to ir.cron, as it would cause deadlocks
|
||||
# during cron job execution. When model_copy() tries to write() on
|
||||
# the recurring,
|
||||
# it has to wait for an ExclusiveLock on the cron job record,
|
||||
# but the latter is locked by the cron system for the duration of
|
||||
# the job!
|
||||
# FIXME: the recurring module should be reviewed to simplify the
|
||||
# scheduling process
|
||||
# and to use a unique cron job for all recurrings, so that it
|
||||
# never needs to be updated during its execution.
|
||||
self.env.cr.execute("ALTER TABLE %s DROP CONSTRAINT %s" % (
|
||||
self._table, '%s_cron_id_fkey' % self._table))
|
||||
|
||||
@api.multi
|
||||
def create_recurring_type(self):
|
||||
rec_doc_obj = self.env['recurring.document']
|
||||
ir_model_id = self.env['ir.model'].search(
|
||||
[('model', '=', self._context.get('active_model', False))])
|
||||
rec_doc_id = rec_doc_obj.search([('model', '=', ir_model_id.id)])
|
||||
if not rec_doc_id:
|
||||
rec_doc_id = rec_doc_obj.create({
|
||||
'name': ir_model_id.name,
|
||||
'model': ir_model_id.id,
|
||||
})
|
||||
return rec_doc_id
|
||||
|
||||
@api.multi
|
||||
def btn_recurring(self):
|
||||
self.ensure_one()
|
||||
rec_doc_id = self.create_recurring_type()
|
||||
if rec_doc_id:
|
||||
active_model = self._context.get('active_model')
|
||||
active_id = self._context.get('active_id')
|
||||
if active_id and active_model:
|
||||
record = self.env[active_model].browse(active_id)
|
||||
self.doc_source = record._name + "," + str(record.id)
|
||||
record.recurring_id = self.id
|
||||
record.rec_source_id = record.id
|
||||
if self._context.get('process') == 'start':
|
||||
self.set_process()
|
||||
|
||||
@api.multi
|
||||
def set_process(self):
|
||||
for recurring in self:
|
||||
model = 'recurring'
|
||||
cron_data = {
|
||||
'name': recurring.name,
|
||||
'interval_number': recurring.interval_number,
|
||||
'interval_type': recurring.interval_type,
|
||||
'numbercall': recurring.exec_init,
|
||||
'nextcall': recurring.date_init,
|
||||
'model_id': self.env['ir.model'].search([('model', '=',
|
||||
model)]).id,
|
||||
'priority': 6,
|
||||
'user_id': recurring.user_id.id,
|
||||
'state': 'code',
|
||||
'code': 'model._cron_model_copy('+repr([recurring.id])+')',
|
||||
}
|
||||
cron = self.env['ir.cron'].sudo().create(cron_data)
|
||||
recurring.write({'cron_id': cron.id, 'state': 'running'})
|
||||
|
||||
@api.multi
|
||||
def set_recurring_id(self):
|
||||
if self.doc_source and 'recurring_id' \
|
||||
in self.env[self.doc_source._name]._fields:
|
||||
rec_id = self.env[self.doc_source._name].browse(self.doc_source.id)
|
||||
if not rec_id.recurring_id:
|
||||
rec_id.recurring_id = self.id
|
||||
else:
|
||||
raise ValidationError(
|
||||
_('Document is already recurring'))
|
||||
|
||||
@api.model
|
||||
def create(self, vals):
|
||||
if vals.get('doc_source', False) and self.search(
|
||||
[('doc_source', '=', vals['doc_source'])]):
|
||||
raise ValidationError(
|
||||
_('Recurring of the selected Source Document already exist'))
|
||||
res = super(Recurring, self).create(vals)
|
||||
res.set_recurring_id()
|
||||
return res
|
||||
|
||||
@api.multi
|
||||
def write(self, values):
|
||||
doc_source_id = False
|
||||
if values.get('doc_source', False):
|
||||
doc_source_id = self.doc_source
|
||||
res = super(Recurring, self).write(values)
|
||||
if doc_source_id:
|
||||
rec_id = self.env[doc_source_id._name].browse(doc_source_id.id)
|
||||
rec_id.recurring_id = False
|
||||
self.set_recurring_id()
|
||||
return res
|
||||
|
||||
@api.multi
|
||||
def get_recurring(self, model, active_id):
|
||||
result = self.env.ref('recurring.action_recurring_form').read()[0]
|
||||
record = self.env[model].browse(active_id)
|
||||
rec_ids = self.env['recurring'].search(
|
||||
[('doc_source', '=', record._name + "," + str(record.id))])
|
||||
result['domain'] = [('id', 'in', rec_ids.ids)]
|
||||
return result
|
||||
|
||||
@api.multi
|
||||
def get_recurring_documents(self, model, action, recurring_id):
|
||||
result = self.env.ref(action).read()[0]
|
||||
res_ids = self.env[model].search(
|
||||
[('recurring_id', '=', recurring_id.id)])
|
||||
result['domain'] = [('id', 'in', res_ids.ids)]
|
||||
return result
|
||||
|
||||
@api.model
|
||||
def _cron_model_copy(self, ids):
|
||||
self.browse(ids).model_copy()
|
||||
|
||||
@api.multi
|
||||
def model_copy(self):
|
||||
for recurring in self.filtered(lambda sub: sub.cron_id):
|
||||
if not recurring.doc_source.exists():
|
||||
raise UserError(_('Please provide another source '
|
||||
'document.\nThis one does not exist!'))
|
||||
|
||||
default = {}
|
||||
documents = self.env['recurring.document'].search(
|
||||
[('model.model', '=', recurring.doc_source._name)], limit=1)
|
||||
fieldnames = dict((f.field.name, f.value == 'date' and
|
||||
fields.Date.today() or False)
|
||||
for f in documents.field_ids)
|
||||
default.update(fieldnames)
|
||||
# if there was only one remaining document to generate
|
||||
# the recurring is over and we mark it as being done
|
||||
if recurring.cron_id.numbercall == 1:
|
||||
recurring.write({'state': 'done'})
|
||||
else:
|
||||
recurring.write({'state': 'running'})
|
||||
copied_doc = recurring.doc_source.copy(default)
|
||||
self.env['recurring.history'].create({
|
||||
'recurring_id': recurring.id,
|
||||
'date': fields.Datetime.now(),
|
||||
'document_id': '%s,%s' % (recurring.doc_source._name,
|
||||
copied_doc.id)})
|
||||
|
||||
@api.multi
|
||||
def unlink(self):
|
||||
if any(self.filtered(lambda s: s.state == "running")):
|
||||
raise UserError(_('You cannot delete an active recurring!'))
|
||||
return super(Recurring, self).unlink()
|
||||
|
||||
@api.multi
|
||||
def set_done(self):
|
||||
self.mapped('cron_id').write({'active': False})
|
||||
self.write({'state': 'done'})
|
||||
|
||||
@api.multi
|
||||
def set_draft(self):
|
||||
self.write({'state': 'draft'})
|
||||
|
||||
|
||||
class RecurringHistory(models.Model):
|
||||
_name = "recurring.history"
|
||||
_description = "Recurring history"
|
||||
_rec_name = 'date'
|
||||
|
||||
date = fields.Datetime(string='Date')
|
||||
recurring_id = fields.Many2one('recurring', string='Recurring',
|
||||
ondelete='cascade')
|
||||
document_id = fields.Reference(
|
||||
selection=_get_document_types, string='Source Document')
|
7
addons/recurring/security/ir.model.access.csv
Normal file
@ -0,0 +1,7 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_recurring_document_fields,access_recurring_document_fields,model_recurring_document_fields,base.group_user,1,1,1,1
|
||||
access_recurring_user,access_recurring_user,model_recurring,base.group_user,1,1,1,1
|
||||
access_recurringhistory_user,access_recurring_history_user,model_recurring_history,base.group_user,1,1,1,1
|
||||
access_recurring_document_user,access_recurring_document_user,model_recurring_document,base.group_user,1,1,1,1
|
||||
access_res_partner_user,access_res_partner_user,base.model_res_partner,base.group_user,1,1,1,1
|
||||
access_ir_cron_user,access_ir_cron_user,base.model_ir_cron,base.group_user,1,0,0,0
|
|
BIN
addons/recurring/static/description/cron_job.png
Normal file
After Width: | Height: | Size: 56 KiB |
BIN
addons/recurring/static/description/icon.png
Normal file
After Width: | Height: | Size: 9.1 KiB |
90
addons/recurring/static/description/index.html
Normal file
@ -0,0 +1,90 @@
|
||||
<section class="container">
|
||||
<div class="row">
|
||||
<h2 class="oe_slogan">Recurring</h2>
|
||||
<h3 class="oe_slogan">Create recurring of document of any module</h3>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="container oe_dark">
|
||||
<div class="row">
|
||||
<h2 class="oe_slogan">Recurring Type</h2>
|
||||
<div class="col-md-6 text-justify mt32">
|
||||
<p class="mt32">
|
||||
To generate recurring document of any module its Recurring type
|
||||
is first created. Here recurring type of sale is created.
|
||||
Recurring type is created from Settings > Technical >
|
||||
Automation > Recurring Types
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<img class="oe_picture oe_screenshot" src="recurring_type.png">
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="container">
|
||||
<div class="row">
|
||||
<h2 class="oe_slogan">Recurring Document</h2>
|
||||
<div class="col-md-6">
|
||||
<img class="oe_picture oe_screenshot" src="recurring_document.png">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<p class="mt32 mb32 text-justify">
|
||||
Recurring Document is created of any document which is
|
||||
needed to be generated everytime at a specific interval of
|
||||
time.
|
||||
Number of documents - The number of times the document
|
||||
should be recurred.
|
||||
Interval Unit - The interval when the recurring should occur.
|
||||
Source Document - Choose the source document on which needs
|
||||
to be generated at every specific interval.
|
||||
Recurring Documents is created from Settings > Technical >
|
||||
Automation > Recurring Documents
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="container oe_dark">
|
||||
<div class="row oe_spaced">
|
||||
<h2 class="oe_slogan">Recurring Document Started</h2>
|
||||
<div class="col-md-6">
|
||||
<p class="mt32">
|
||||
Recurring of the selected document can be started any time
|
||||
by clicking on the 'Process' Button
|
||||
Cron job is automatically created and is linked here.
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<img class="oe_picture oe_screenshot"
|
||||
src="recurring_document_started.png"/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="container">
|
||||
<div class="row">
|
||||
<h2 class="oe_slogan">Scheduled Actions</h2>
|
||||
<div class="col-md-6">
|
||||
<img class="oe_picture oe_screenshot" src="cron_job.png">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<p class="oe_mt8 text-justify">The generated cron job from the
|
||||
recurring document</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="container">
|
||||
<div class="row">
|
||||
<h2 class="oe_slogan">Sale Orders</h2>
|
||||
<div class="col-md-6">
|
||||
<p class="mt32 text-justify">
|
||||
Sale Orders that are generated through recurring.
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<img class="oe_picture oe_screenshot" src="sale_order.png"/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
BIN
addons/recurring/static/description/recurring_document.png
Normal file
After Width: | Height: | Size: 50 KiB |
After Width: | Height: | Size: 52 KiB |
BIN
addons/recurring/static/description/recurring_type.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
addons/recurring/static/description/sale_order.png
Normal file
After Width: | Height: | Size: 34 KiB |
4
addons/recurring/tests/__init__.py
Normal file
@ -0,0 +1,4 @@
|
||||
# Part of Flectra. See LICENSE file for full copyright and licensing
|
||||
# details.
|
||||
|
||||
from . import test_recurring
|
36
addons/recurring/tests/test_recurring.py
Normal file
@ -0,0 +1,36 @@
|
||||
# Part of Flectra. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from flectra import _
|
||||
from flectra.exceptions import UserError
|
||||
|
||||
from flectra.tests.common import TransactionCase
|
||||
import logging
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class RecurringTransactionCase(TransactionCase):
|
||||
|
||||
def setUp(self):
|
||||
super(RecurringTransactionCase, self).setUp()
|
||||
self.recurring_id = self.env.ref("recurring.recurring_partner0")
|
||||
|
||||
def check_recurring(self):
|
||||
self.recurring_id.set_process()
|
||||
if not self.recurring_id.cron_id:
|
||||
raise UserError(_("Cron is not created"))
|
||||
else:
|
||||
self.recurring_id.cron_id.method_direct_trigger()
|
||||
try:
|
||||
self.recurring_id.unlink()
|
||||
except Exception as e:
|
||||
_logger.info(
|
||||
"You cannot delete an active recurring!")
|
||||
self.recurring_id.set_done()
|
||||
if not self.recurring_id.state == 'done':
|
||||
raise UserError(_("Recurring is in running state"))
|
||||
self.recurring_id.set_draft()
|
||||
if not self.recurring_id.state == 'draft':
|
||||
raise UserError(_("Recurring is not in draft state"))
|
||||
|
||||
def test_01_recurring(self):
|
||||
self.check_recurring()
|
275
addons/recurring/views/recurring_view.xml
Normal file
@ -0,0 +1,275 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<flectra>
|
||||
|
||||
<!-- Recurring Views -->
|
||||
<record id="view_recurring_form" model="ir.ui.view">
|
||||
<field name="name">recurring.form</field>
|
||||
<field name="model">recurring</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Recurrings">
|
||||
<header>
|
||||
<button name="set_process" states="draft" string="Process"
|
||||
type="object" class="oe_highlight"/>
|
||||
<button name="set_done" states="running" string="Stop"
|
||||
type="object" class="oe_highlight"/>
|
||||
<button name="set_draft" states="done"
|
||||
string="Set to Draft" type="object"/>
|
||||
<field name="state" widget="statusbar"
|
||||
statusbar_visible="draft,running"/>
|
||||
</header>
|
||||
<sheet>
|
||||
<group col="4">
|
||||
<field name="name" required="1"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="user_id" required="1"/>
|
||||
<field name="active"/>
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="Recurring Data">
|
||||
<group col="4">
|
||||
<field name="interval_number"/>
|
||||
<field name="interval_type"/>
|
||||
<field name="exec_init"/>
|
||||
<field name="date_init"/>
|
||||
<field name="doc_source" required="1"/>
|
||||
<field name="cron_id"/>
|
||||
</group>
|
||||
<separator string="Internal Notes"/>
|
||||
<field name="notes"/>
|
||||
</page>
|
||||
<page string="Documents created">
|
||||
<field name="doc_lines" widget="one2many_list"
|
||||
readonly="1"/>
|
||||
</page>
|
||||
<page string="Notes">
|
||||
<field name="note"/>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_recurring_tree" model="ir.ui.view">
|
||||
<field name="name">recurring.tree</field>
|
||||
<field name="model">recurring</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Recurrings">
|
||||
<field name="name"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="active"/>
|
||||
<field name="user_id"/>
|
||||
<field name="state"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_recurring_filter" model="ir.ui.view">
|
||||
<field name="name">recurring.filter</field>
|
||||
<field name="model">recurring</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Search Recurring">
|
||||
<field name="name" string="Recurring"/>
|
||||
<field name="date_init"/>
|
||||
<filter string="Running"
|
||||
domain="[('state','=','running')]"/>
|
||||
<field name="user_id"/>
|
||||
<field name="partner_id"/>
|
||||
<group expand="0" string="Group By">
|
||||
<filter string="User" name="User" domain="[]"
|
||||
context="{'group_by':'user_id'}"/>
|
||||
<filter string="Partner" name="Partner" domain="[]"
|
||||
context="{'group_by':'partner_id'}"/>
|
||||
<filter string="Status" domain="[]"
|
||||
context="{'group_by':'state'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_recurring_form" model="ir.actions.act_window">
|
||||
<field name="name">Recurring Documents</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">recurring</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_id" ref="view_recurring_tree"/>
|
||||
<field name="context">{'search_default_User':1,
|
||||
'disable':False}
|
||||
</field>
|
||||
<field name="search_view_id" ref="view_recurring_filter"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window.view"
|
||||
id="action_recurring_tree_view">
|
||||
<field name="sequence" eval="2"/>
|
||||
<field name="view_mode">tree</field>
|
||||
<field name="view_id" ref="view_recurring_tree"/>
|
||||
<field name="act_window_id" ref="action_recurring_form"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window.view"
|
||||
id="action_recurring_form_view">
|
||||
<field name="sequence" eval="2"/>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="view_recurring_form"/>
|
||||
<field name="act_window_id" ref="action_recurring_form"/>
|
||||
</record>
|
||||
|
||||
<menuitem action="action_recurring_form" id="menu_action_recurring_form"
|
||||
parent="base.menu_automation"/>
|
||||
|
||||
<record id="recurring_history_tree" model="ir.ui.view">
|
||||
<field name="name">recurring.history.tree</field>
|
||||
<field name="model">recurring.history</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Recurring History">
|
||||
<field name="date"/>
|
||||
<field name="document_id"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="recurring_history_form" model="ir.ui.view">
|
||||
<field name="name">recurring.history.form</field>
|
||||
<field name="model">recurring.history</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Recurring History">
|
||||
<group col="4">
|
||||
<field name="date"/>
|
||||
<field name="document_id" required="1"/>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="document_form" model="ir.ui.view">
|
||||
<field name="name">recurring.document.form</field>
|
||||
<field name="model">recurring.document</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Recurring Document">
|
||||
<group col="4">
|
||||
<field name="name" required="1"/>
|
||||
<field name="model" required="1"/>
|
||||
<field name="active"/>
|
||||
</group>
|
||||
<field name="field_ids"/>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="document_tree" model="ir.ui.view">
|
||||
<field name="name">recurring.document.tree</field>
|
||||
<field name="model">recurring.document</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Recurring Document">
|
||||
<field name="name"/>
|
||||
<field name="active"/>
|
||||
<field name="model"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_recurring_document_filter" model="ir.ui.view">
|
||||
<field name="name">recurring.document.filter</field>
|
||||
<field name="model">recurring.document</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Recurring Document">
|
||||
<field name="name" string="Recurring Document"/>
|
||||
<field name="model"/>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="document_fields_form" model="ir.ui.view">
|
||||
<field name="name">recurring.document.fields.form</field>
|
||||
<field name="model">recurring.document.fields</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Recurring Document Fields">
|
||||
<group>
|
||||
<field name="field" required="1"/>
|
||||
<field name="value"/>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="document_fields_tree" model="ir.ui.view">
|
||||
<field name="name">recurring.document.fields.tree</field>
|
||||
<field name="model">recurring.document.fields</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Recurring Document Fields">
|
||||
<field name="field"/>
|
||||
<field name="value"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_document_form" model="ir.actions.act_window">
|
||||
<field name="name">Recurring Types</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">recurring.document</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_id" ref="document_tree"/>
|
||||
<field name="search_view_id" ref="view_recurring_document_filter"/>
|
||||
</record>
|
||||
<menuitem id="menu_action_document_form" action="action_document_form"
|
||||
parent="base.menu_automation"/>
|
||||
|
||||
<record id="view_recurring_wizard_form" model="ir.ui.view">
|
||||
<field name="name">recurring</field>
|
||||
<field name="model">recurring</field>
|
||||
<field name="type">form</field>
|
||||
<field name="priority" eval="8"/>
|
||||
<field name="arch" type="xml">
|
||||
<form string="IR Recurring">
|
||||
<div class="oe_title">
|
||||
<h1>
|
||||
<field name="name" readonly="1" force_save="1"/>
|
||||
</h1>
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<field name="partner_id"/>
|
||||
<field name="user_id" required="1"/>
|
||||
<field name="active"/>
|
||||
<field name="exec_init"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="interval_number"/>
|
||||
<field name="interval_type"/>
|
||||
<field name="date_init"/>
|
||||
<field name="doc_source" invisible="1"/>
|
||||
</group>
|
||||
</group>
|
||||
|
||||
<footer>
|
||||
<button name="btn_recurring" type="object"
|
||||
string="Start Recurring" class="oe_highlight"
|
||||
context="{'process':'start'}"/>
|
||||
<button name="btn_recurring" type="object"
|
||||
string="Save"/>
|
||||
<button string="Cancel" special="cancel"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_recurring_wizard" model="ir.actions.act_window">
|
||||
<field name="name">Recurring Documents</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">recurring</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="view_recurring_wizard_form"/>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window.view"
|
||||
id="action_recurring_wizard_form_view">
|
||||
<field name="sequence" eval="2"/>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="view_recurring_wizard_form"/>
|
||||
<field name="act_window_id" ref="action_recurring_wizard"/>
|
||||
</record>
|
||||
|
||||
</flectra>
|
3
addons/recurring_account/__init__.py
Normal file
@ -0,0 +1,3 @@
|
||||
# Part of Flectra. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import models
|
20
addons/recurring_account/__manifest__.py
Normal file
@ -0,0 +1,20 @@
|
||||
# Part of Flectra. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
{
|
||||
'name': 'Account Invoice Recurrings',
|
||||
'version': '1.0',
|
||||
'category': 'Extra Tools',
|
||||
'summary': 'Create recurring of invoices',
|
||||
'description': """
|
||||
Account Invoice Recurring allows to automatically create
|
||||
recurring of any particular invoice
|
||||
""",
|
||||
'author': 'FlectraHQ',
|
||||
'website': 'https://www.flectrahq.com/',
|
||||
'depends': ['account_invoicing', 'recurring'],
|
||||
'data': [
|
||||
'views/account_invoice_view.xml',
|
||||
],
|
||||
'installable': True,
|
||||
'auto_install': False,
|
||||
}
|
45
addons/recurring_account/i18n/recurring_account.pot
Normal file
@ -0,0 +1,45 @@
|
||||
# Translation of Flectra Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * recurring_account
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Flectra Server 1.3\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-08-01 17:10+0000\n"
|
||||
"PO-Revision-Date: 2018-08-01 17:10+0000\n"
|
||||
"Last-Translator: <>\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"Plural-Forms: \n"
|
||||
|
||||
#. module: recurring_account
|
||||
#: model:ir.ui.view,arch_db:recurring_account.invoice_form_inherit
|
||||
#: model:ir.ui.view,arch_db:recurring_account.invoice_supplier_form_inherit
|
||||
msgid "Document"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring_account
|
||||
#: model:ir.ui.view,arch_db:recurring_account.invoice_form_inherit
|
||||
#: model:ir.ui.view,arch_db:recurring_account.invoice_supplier_form_inherit
|
||||
msgid "Generate Recurring"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring_account
|
||||
#: model:ir.model,name:recurring_account.model_account_invoice
|
||||
msgid "Invoice"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring_account
|
||||
#: model:ir.ui.view,arch_db:recurring_account.invoice_form_inherit
|
||||
#: model:ir.ui.view,arch_db:recurring_account.invoice_supplier_form_inherit
|
||||
msgid "Recurring"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring_account
|
||||
#: model:ir.model.fields,field_description:recurring_account.field_account_invoice_recurring_id
|
||||
msgid "Recurring Reference"
|
||||
msgstr ""
|
||||
|
3
addons/recurring_account/models/__init__.py
Normal file
@ -0,0 +1,3 @@
|
||||
# Part of Flectra. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import account_invoice
|
32
addons/recurring_account/models/account_invoice.py
Normal file
@ -0,0 +1,32 @@
|
||||
# Part of Flectra. See LICENSE file for full copyright and licensing
|
||||
# details.
|
||||
|
||||
from flectra import api, models, fields
|
||||
|
||||
|
||||
class AccountInvoice(models.Model):
|
||||
_inherit = "account.invoice"
|
||||
|
||||
recurring_id = fields.Many2one('recurring', string='Recurring Reference')
|
||||
rec_source_id = fields.Many2one('account.invoice',
|
||||
string='Recurring Source')
|
||||
|
||||
@api.multi
|
||||
def get_recurring(self):
|
||||
result = self.env['recurring'].get_recurring(
|
||||
'account.invoice', self.id)
|
||||
return result
|
||||
|
||||
@api.multi
|
||||
def get_recurring_documents(self):
|
||||
result = self.env['recurring'].get_recurring_documents(
|
||||
'account.invoice', 'account.action_invoice_tree1',
|
||||
self.recurring_id)
|
||||
return result
|
||||
|
||||
@api.multi
|
||||
def action_invoice_cancel(self):
|
||||
res = super(AccountInvoice, self).action_invoice_cancel()
|
||||
if self.recurring_id:
|
||||
self.recurring_id.set_done()
|
||||
return res
|
BIN
addons/recurring_account/static/description/cron_job.png
Normal file
After Width: | Height: | Size: 43 KiB |
BIN
addons/recurring_account/static/description/icon.png
Normal file
After Width: | Height: | Size: 7.2 KiB |
99
addons/recurring_account/static/description/index.html
Normal file
@ -0,0 +1,99 @@
|
||||
<section class="container">
|
||||
<div class="row">
|
||||
<h2 class="oe_slogan">Recurring Invoices</h2>
|
||||
<h3 class="oe_slogan">Create recurring invoices</h3>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="container oe_dark">
|
||||
<div class="row">
|
||||
<h2 class="oe_slogan">Recurring Wizard</h2>
|
||||
<div class="col-md-6 text-justify mt32">
|
||||
<p class="mt32">
|
||||
A 'Generate Recurring' button is displayed on the invoice.<br/>
|
||||
Recurring Document, Recurring Type and Scheduled action of this
|
||||
particular invoice is automatically created.<br/>
|
||||
Number of documents - The number of times the document
|
||||
should be recurred.<br/>
|
||||
Interval Unit - The interval when the recurring should
|
||||
occur.<br/>
|
||||
Start Recurring - Activates the generated recurring.<br/>
|
||||
Save - Creates the recurring but doesn't activates it.
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<img class="oe_picture oe_screenshot" src="recurring_document_wizard1.png">
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="container">
|
||||
<div class="row">
|
||||
<h2 class="oe_slogan">Invoice</h2>
|
||||
<div class="col-md-6">
|
||||
<img class="oe_picture oe_screenshot" src="invoice.png">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<p class="mt32 mb32 text-justify">
|
||||
Recurring - Shows the generated recurring.<br/>
|
||||
Document - Links the invoices that are generated by the
|
||||
recurring of that particular invoice.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="container oe_dark">
|
||||
<div class="row oe_spaced">
|
||||
<h2 class="oe_slogan">Invoice Recurring Documents</h2>
|
||||
<div class="col-md-6">
|
||||
<p class="mt32">
|
||||
Invoices that are generated through recurring are linked here.
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<img class="oe_picture oe_screenshot"
|
||||
src="recurring_documents.png"/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="container">
|
||||
<div class="row">
|
||||
<h2 class="oe_slogan">Recurring Document</h2>
|
||||
<div class="col-md-6">
|
||||
<img class="oe_picture oe_screenshot" src="rec_doc.png"/>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<p class="mt32 text-justify">
|
||||
Recurring Document is automatically created.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="container">
|
||||
<div class="row">
|
||||
<h2 class="oe_slogan">Recurring Type</h2>
|
||||
<div class="col-md-6">
|
||||
<p class="oe_mt8 text-justify">Recurring Type is automatically
|
||||
created</p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<img class="oe_picture oe_screenshot" src="rec_type.png">
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="container">
|
||||
<div class="row">
|
||||
<h2 class="oe_slogan">Scheduled Actions</h2>
|
||||
<div class="col-md-6">
|
||||
<img class="oe_picture oe_screenshot" src="cron_job.png">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<p class="oe_mt8 text-justify">Cron job is automatically
|
||||
created</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
BIN
addons/recurring_account/static/description/invoice.png
Normal file
After Width: | Height: | Size: 78 KiB |
BIN
addons/recurring_account/static/description/rec_doc.png
Normal file
After Width: | Height: | Size: 33 KiB |
BIN
addons/recurring_account/static/description/rec_type.png
Normal file
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 36 KiB |
After Width: | Height: | Size: 26 KiB |
64
addons/recurring_account/views/account_invoice_view.xml
Normal file
@ -0,0 +1,64 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<flectra>
|
||||
|
||||
<record id="invoice_form_inherit" model="ir.ui.view">
|
||||
<field name="name">account.invoice.form.inherit</field>
|
||||
<field name="model">account.invoice</field>
|
||||
<field name="inherit_id" ref="account.invoice_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='user_id']" position="after">
|
||||
<field name="recurring_id" invisible="1"/>
|
||||
<field name="rec_source_id" readonly="1"/>
|
||||
</xpath>
|
||||
<xpath expr="//button[@name='action_invoice_open']"
|
||||
position="after">
|
||||
<button name="%(recurring.action_recurring_wizard)d"
|
||||
id="generate_recurring"
|
||||
string="Generate Recurring" class="btn-primary"
|
||||
type="action"
|
||||
attrs="{'invisible': ['|', ('state', 'in', ['draft','cancel']), ('recurring_id', '!=', False)]}"/>
|
||||
</xpath>
|
||||
<xpath expr="//sheet/div[@name='button_box']" position="inside">
|
||||
<button class="oe_stat_button" type="object"
|
||||
string="Recurring" name="get_recurring"
|
||||
icon="fa-tasks" attrs="{'invisible': [('recurring_id', '=', False)]}"/>
|
||||
|
||||
<button class="oe_stat_button" type="object"
|
||||
string="Document" name="get_recurring_documents"
|
||||
icon="fa-tasks" attrs="{'invisible': [('recurring_id', '=', False)]}"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="invoice_supplier_form_inherit" model="ir.ui.view">
|
||||
<field name="name">account.invoice.supplier.form.inherit</field>
|
||||
<field name="model">account.invoice</field>
|
||||
<field name="inherit_id" ref="account.invoice_supplier_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='date_due']" position="after">
|
||||
<field name="recurring_id" invisible="1"/>
|
||||
<field name="rec_source_id" readonly="1"/>
|
||||
</xpath>
|
||||
<xpath expr="//button[@name='action_invoice_open']"
|
||||
position="after">
|
||||
<button name="%(recurring.action_recurring_wizard)d"
|
||||
id="generate_recurring"
|
||||
string="Generate Recurring" class="btn-primary"
|
||||
type="action"
|
||||
attrs="{'invisible': ['|', ('state', 'in', ['draft','cancel']), ('recurring_id', '!=', False)]}"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='type']" position="before">
|
||||
<div class="oe_button_box" name="button_box">
|
||||
<button class="oe_stat_button" type="object"
|
||||
string="Recurring" name="get_recurring"
|
||||
icon="fa-tasks" attrs="{'invisible': [('recurring_id', '=', False)]}"/>
|
||||
|
||||
<button class="oe_stat_button" type="object"
|
||||
string="Document" name="get_recurring_documents"
|
||||
icon="fa-tasks" attrs="{'invisible': [('recurring_id', '=', False)]}"/>
|
||||
</div>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</flectra>
|
3
addons/recurring_purchase/__init__.py
Normal file
@ -0,0 +1,3 @@
|
||||
# Part of Flectra. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import models
|
21
addons/recurring_purchase/__manifest__.py
Normal file
@ -0,0 +1,21 @@
|
||||
# Part of Flectra. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
{
|
||||
'name': 'Purchase Order Recurring',
|
||||
'version': '1.0',
|
||||
'category': 'Extra Tools',
|
||||
'summary': 'Create recurring of purchase order',
|
||||
'description': """
|
||||
Purchase Order Recurring allows to automatically create recurring of
|
||||
any particular purchase order
|
||||
""",
|
||||
'author': 'FlectraHQ',
|
||||
'website': 'https://www.flectrahq.com/',
|
||||
'depends': ['recurring', 'purchase'],
|
||||
'data': [
|
||||
'views/purchase_views.xml',
|
||||
],
|
||||
'demo': ['demo/purchase_recurring_demo.xml'],
|
||||
'installable': True,
|
||||
'auto_install': False,
|
||||
}
|
28
addons/recurring_purchase/demo/purchase_recurring_demo.xml
Normal file
@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" ?>
|
||||
<flectra noupdate="1">
|
||||
|
||||
<record id="purchase_order_demo" model="purchase.order">
|
||||
<field name="name">PO001</field>
|
||||
<field name="partner_id" ref="base.res_partner_2"/>
|
||||
</record>
|
||||
|
||||
<record id="purchase_order_line_demo" model="purchase.order.line">
|
||||
<field name="order_id"
|
||||
ref="recurring_purchase.purchase_order_demo"/>
|
||||
<field name="name">[RAM-SR5] RAM DDR SR5</field>
|
||||
<field name="date_planned" eval="time.strftime('%Y/%m/10')"/>
|
||||
<field name="product_id" ref="product.product_product_13"/>
|
||||
<field name="product_uom" ref="product.product_uom_unit"/>
|
||||
<field name="price_unit">60</field>
|
||||
<field name="product_qty">5</field>
|
||||
</record>
|
||||
|
||||
<record id="recurring_purchase1" model="recurring">
|
||||
<field name="name">PO001</field>
|
||||
<field name="exec_init">2</field>
|
||||
<field name="interval_type">minutes</field>
|
||||
<field name="date_init" eval="datetime.now()"/>
|
||||
<field name="doc_source" ref='recurring_purchase.purchase_order_demo'/>
|
||||
</record>
|
||||
|
||||
</flectra>
|
42
addons/recurring_purchase/i18n/recurring_purchase.pot
Normal file
@ -0,0 +1,42 @@
|
||||
# Translation of Flectra Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * recurring_purchase
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Flectra Server 1.3\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-08-01 16:52+0000\n"
|
||||
"PO-Revision-Date: 2018-08-01 16:52+0000\n"
|
||||
"Last-Translator: <>\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"Plural-Forms: \n"
|
||||
|
||||
#. module: recurring_purchase
|
||||
#: model:ir.ui.view,arch_db:recurring_purchase.view_purchase_order_form_inherit
|
||||
msgid "Document"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring_purchase
|
||||
#: model:ir.ui.view,arch_db:recurring_purchase.view_purchase_order_form_inherit
|
||||
msgid "Generate Recurring"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring_purchase
|
||||
#: model:ir.model,name:recurring_purchase.model_purchase_order
|
||||
msgid "Purchase Order"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring_purchase
|
||||
#: model:ir.ui.view,arch_db:recurring_purchase.view_purchase_order_form_inherit
|
||||
msgid "Recurring"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring_purchase
|
||||
#: model:ir.model.fields,field_description:recurring_purchase.field_purchase_order_recurring_id
|
||||
msgid "Recurring Reference"
|
||||
msgstr ""
|
||||
|
3
addons/recurring_purchase/models/__init__.py
Normal file
@ -0,0 +1,3 @@
|
||||
# Part of Flectra. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import purchase
|
28
addons/recurring_purchase/models/purchase.py
Normal file
@ -0,0 +1,28 @@
|
||||
# Part of Flectra. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from flectra import api, models, fields
|
||||
|
||||
|
||||
class PurchaseOrder(models.Model):
|
||||
_inherit = 'purchase.order'
|
||||
|
||||
recurring_id = fields.Many2one('recurring', string='Recurring Reference')
|
||||
rec_source_id = fields.Many2one('purchase.order',
|
||||
string='Recurring Source')
|
||||
|
||||
@api.multi
|
||||
def get_recurring(self):
|
||||
result = self.env['recurring'].get_recurring('purchase.order', self.id)
|
||||
return result
|
||||
|
||||
@api.multi
|
||||
def get_recurring_documents(self):
|
||||
result = self.env['recurring'].get_recurring_documents(
|
||||
'purchase.order', 'purchase.purchase_rfq', self.recurring_id)
|
||||
return result
|
||||
|
||||
@api.multi
|
||||
def button_cancel(self):
|
||||
if self.recurring_id:
|
||||
self.recurring_id.set_done()
|
||||
return super(PurchaseOrder, self).button_cancel()
|
BIN
addons/recurring_purchase/static/description/cron_job.png
Normal file
After Width: | Height: | Size: 42 KiB |
BIN
addons/recurring_purchase/static/description/icon.png
Normal file
After Width: | Height: | Size: 8.6 KiB |
103
addons/recurring_purchase/static/description/index.html
Normal file
@ -0,0 +1,103 @@
|
||||
<section class="container">
|
||||
<div class="row">
|
||||
<h2 class="oe_slogan">Recurring Purchase</h2>
|
||||
<h3 class="oe_slogan">Create recurring purchase order</h3>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="container oe_dark">
|
||||
<div class="row">
|
||||
<h2 class="oe_slogan">Recurring Wizard</h2>
|
||||
<div class="col-md-6">
|
||||
<p class="mt32 mb32 text-justify">
|
||||
A 'Generate Recurring' button is displayed on the purchase
|
||||
order.<br/>
|
||||
Recurring Document, Recurring Type and Scheduled action of this
|
||||
particular purchase order is automatically created.<br/>
|
||||
Number of documents - The number of times the document
|
||||
should be recurred.<br/>
|
||||
Interval Unit - The interval when the recurring should
|
||||
occur.<br/>
|
||||
Start Recurring - Activates the generated recurring.<br/>
|
||||
Save - Creates the recurring but doesn't activates it.
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<img class="oe_picture oe_screenshot"
|
||||
src="recurring_document_wizard1.png">
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="container">
|
||||
<div class="row">
|
||||
<h2 class="oe_slogan">Purchase Order</h2>
|
||||
<div class="col-md-6">
|
||||
<img class="oe_picture oe_screenshot" src="purchase_order.png">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<p class="mt32 mb32 text-justify">
|
||||
Recurring - Shows the generated recurring.<br/>
|
||||
Document - Links the purchase order that are generated by the
|
||||
recurring of that particular purchase order.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="container oe_dark">
|
||||
<div class="row oe_spaced">
|
||||
<h2 class="oe_slogan">PO Recurring Documents</h2>
|
||||
<div class="col-md-6">
|
||||
<p class="mt32">
|
||||
Purchase Orders that are generated through recurring.
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<img class="oe_picture oe_screenshot"
|
||||
src="recurring_documents.png"/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="container">
|
||||
<div class="row">
|
||||
<h2 class="oe_slogan">Recurring Document</h2>
|
||||
<div class="col-md-6">
|
||||
<img class="oe_picture oe_screenshot" src="rec_doc.png">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<p class="oe_mt8 text-justify">Recurring Document is
|
||||
automatically created and is linked with the purchase order
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="container">
|
||||
<div class="row">
|
||||
<h2 class="oe_slogan">Recurring Type</h2>
|
||||
<div class="col-md-6">
|
||||
<p class="mt32 text-justify">
|
||||
Recurring Type is automatically
|
||||
created
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<img class="oe_picture oe_screenshot" src="rec_type.png"/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="container">
|
||||
<div class="row">
|
||||
<h2 class="oe_slogan">Scheduled Actions</h2>
|
||||
<div class="col-md-6">
|
||||
<img class="oe_picture oe_screenshot" src="cron_job.png">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<p class="oe_mt8 text-justify">Cron job is automatically
|
||||
created</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
BIN
addons/recurring_purchase/static/description/purchase_order.png
Normal file
After Width: | Height: | Size: 63 KiB |
BIN
addons/recurring_purchase/static/description/rec_doc.png
Normal file
After Width: | Height: | Size: 35 KiB |
BIN
addons/recurring_purchase/static/description/rec_type.png
Normal file
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 36 KiB |
After Width: | Height: | Size: 26 KiB |
3
addons/recurring_purchase/tests/__init__.py
Normal file
@ -0,0 +1,3 @@
|
||||
# Part of Flectra. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import test_puchase_recurring
|
21
addons/recurring_purchase/tests/test_puchase_recurring.py
Normal file
@ -0,0 +1,21 @@
|
||||
# Part of Flectra. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from flectra.tests.common import TransactionCase
|
||||
|
||||
|
||||
class PurchaseRecurringTransactionCase(TransactionCase):
|
||||
|
||||
def setUp(self):
|
||||
super(PurchaseRecurringTransactionCase, self).setUp()
|
||||
self.purchase_order = self.env.ref(
|
||||
"purchase.purchase_order_2")
|
||||
|
||||
def create_purchase_recurring(self):
|
||||
recurring_id = self.env['recurring'].create({
|
||||
'name': 'PO00002',
|
||||
'exec_init': 5,
|
||||
})
|
||||
recurring_id.btn_recurring()
|
||||
|
||||
def test_01_recurring(self):
|
||||
self.create_purchase_recurring()
|
34
addons/recurring_purchase/views/purchase_views.xml
Normal file
@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<flectra>
|
||||
|
||||
<record id="view_purchase_order_form_inherit" model="ir.ui.view">
|
||||
<field name="name">purchase.order.form.inherit</field>
|
||||
<field name="model">purchase.order</field>
|
||||
<field name="inherit_id" ref="purchase.purchase_order_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='partner_ref']" position="after">
|
||||
<field name="recurring_id" invisible="1"/>
|
||||
<field name="rec_source_id" readonly="1"/>
|
||||
</xpath>
|
||||
<xpath expr="//button[@name='button_confirm']" position="before">
|
||||
<button name="%(recurring.action_recurring_wizard)d"
|
||||
id="generate_recurring"
|
||||
string="Generate Recurring" class="btn-primary"
|
||||
type="action"
|
||||
attrs="{'invisible': ['|', ('state', 'in', ['draft', 'cancel']), ('recurring_id', '!=', False)]}"/>
|
||||
</xpath>
|
||||
<xpath expr="//sheet/div[@name='button_box']" position="inside">
|
||||
<button class="oe_stat_button" type="object"
|
||||
string="Recurring"
|
||||
name="get_recurring"
|
||||
icon="fa-tasks" attrs="{'invisible': [('recurring_id', '=', False)]}"/>
|
||||
|
||||
<button class="oe_stat_button" type="object"
|
||||
string="Document"
|
||||
name="get_recurring_documents"
|
||||
icon="fa-tasks" attrs="{'invisible': [('recurring_id', '=', False)]}"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</flectra>
|
3
addons/recurring_sale/__init__.py
Normal file
@ -0,0 +1,3 @@
|
||||
# Part of Flectra. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import models
|
21
addons/recurring_sale/__manifest__.py
Normal file
@ -0,0 +1,21 @@
|
||||
# Part of Flectra. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
{
|
||||
'name': 'Sale Order Recurring',
|
||||
'version': '1.0',
|
||||
'category': 'Extra Tools',
|
||||
'summary': 'Create recurring of sale order',
|
||||
'description': """
|
||||
Sale Order Recurring allows to automatically create recurring of
|
||||
any particular sale order
|
||||
""",
|
||||
'author': 'FlectraHQ',
|
||||
'website': 'https://www.flectrahq.com/',
|
||||
'depends': ['sale_management', 'recurring'],
|
||||
'data': [
|
||||
'views/sale_views.xml',
|
||||
],
|
||||
'demo': ['demo/sale_recurring_demo.xml'],
|
||||
'installable': True,
|
||||
'auto_install': False,
|
||||
}
|
12
addons/recurring_sale/demo/sale_recurring_demo.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" ?>
|
||||
<flectra noupdate="1">
|
||||
|
||||
<record id="recurring_sale1" model="recurring">
|
||||
<field name="name">SO001</field>
|
||||
<field name="exec_init">2</field>
|
||||
<field name="interval_type">minutes</field>
|
||||
<field name="date_init" eval="datetime.now()"/>
|
||||
<field name="doc_source" ref='sale.sale_order_2'/>
|
||||
</record>
|
||||
|
||||
</flectra>
|
42
addons/recurring_sale/i18n/recurring_sale.pot
Normal file
@ -0,0 +1,42 @@
|
||||
# Translation of Flectra Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * recurring_sale
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Flectra Server 1.3\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-08-01 16:38+0000\n"
|
||||
"PO-Revision-Date: 2018-08-01 16:38+0000\n"
|
||||
"Last-Translator: <>\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"Plural-Forms: \n"
|
||||
|
||||
#. module: recurring_sale
|
||||
#: model:ir.ui.view,arch_db:recurring_sale.view_order_form_inherit
|
||||
msgid "Document"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring_sale
|
||||
#: model:ir.ui.view,arch_db:recurring_sale.view_order_form_inherit
|
||||
msgid "Generate Recurring"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring_sale
|
||||
#: model:ir.model,name:recurring_sale.model_sale_order
|
||||
msgid "Quotation"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring_sale
|
||||
#: model:ir.ui.view,arch_db:recurring_sale.view_order_form_inherit
|
||||
msgid "Recurring"
|
||||
msgstr ""
|
||||
|
||||
#. module: recurring_sale
|
||||
#: model:ir.model.fields,field_description:recurring_sale.field_sale_order_recurring_id
|
||||
msgid "Recurring Reference"
|
||||
msgstr ""
|
||||
|
3
addons/recurring_sale/models/__init__.py
Normal file
@ -0,0 +1,3 @@
|
||||
# Part of Flectra. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import sale
|
28
addons/recurring_sale/models/sale.py
Normal file
@ -0,0 +1,28 @@
|
||||
# Part of Flectra. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from flectra import api, models, fields
|
||||
|
||||
|
||||
class SaleOrder(models.Model):
|
||||
_inherit = "sale.order"
|
||||
|
||||
recurring_id = fields.Many2one('recurring', string='Recurring Reference')
|
||||
rec_source_id = fields.Many2one('sale.order', string='Recurring Source')
|
||||
|
||||
@api.multi
|
||||
def get_recurring(self):
|
||||
result = self.env['recurring'].get_recurring('sale.order', self.id)
|
||||
return result
|
||||
|
||||
@api.multi
|
||||
def get_recurring_documents(self):
|
||||
result = self.env['recurring'].get_recurring_documents(
|
||||
'sale.order', 'sale.action_quotations', self.recurring_id)
|
||||
return result
|
||||
|
||||
@api.multi
|
||||
def action_cancel(self):
|
||||
res = super(SaleOrder, self).action_cancel()
|
||||
if self.recurring_id:
|
||||
self.recurring_id.set_done()
|
||||
return res
|
BIN
addons/recurring_sale/static/description/cron_job.png
Normal file
After Width: | Height: | Size: 38 KiB |
BIN
addons/recurring_sale/static/description/icon.png
Normal file
After Width: | Height: | Size: 10 KiB |
101
addons/recurring_sale/static/description/index.html
Normal file
@ -0,0 +1,101 @@
|
||||
<section class="container">
|
||||
<div class="row">
|
||||
<h2 class="oe_slogan">Recurring Sale</h2>
|
||||
<h3 class="oe_slogan">Create recurring sale order</h3>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="container oe_dark">
|
||||
<div class="row">
|
||||
<h2 class="oe_slogan">Recurring Wizard</h2>
|
||||
<div class="col-md-6 text-justify mt32">
|
||||
<p class="mt32">
|
||||
A 'Generate Recurring' button is displayed on the sale
|
||||
order.<br/>
|
||||
Recurring Document, Recurring Type and Scheduled action of this
|
||||
particular sale order is automatically created.<br/>
|
||||
Number of documents - The number of times the document
|
||||
should be recurred.<br/>
|
||||
Interval Unit - The interval when the recurring should
|
||||
occur.<br/>
|
||||
Start Recurring - Activates the generated recurring.<br/>
|
||||
Save - Creates the recurring but doesn't activates it.
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<img class="oe_picture oe_screenshot"
|
||||
src="recurring_document_wizard1.png">
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="container">
|
||||
<div class="row">
|
||||
<h2 class="oe_slogan">Sale Order</h2>
|
||||
<div class="col-md-6">
|
||||
<img class="oe_picture oe_screenshot" src="sale_order.png">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<p class="mt32 mb32 text-justify">
|
||||
Recurring - Shows the generated recurring.<br/>
|
||||
Document - Links the sale order that are generated by the
|
||||
recurring of that particular sale order.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="container oe_dark">
|
||||
<div class="row oe_spaced">
|
||||
<h2 class="oe_slogan">SO Recurring Documents</h2>
|
||||
<div class="col-md-6">
|
||||
<p class="mt32">
|
||||
Sale Orders that are generated through recurring.
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<img class="oe_picture oe_screenshot"
|
||||
src="recurring_documents.png"/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="container">
|
||||
<div class="row">
|
||||
<h2 class="oe_slogan">Recurring Document</h2>
|
||||
<div class="col-md-6">
|
||||
<img class="oe_picture oe_screenshot" src="rec_doc.png"/>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<p class="mt32 text-justify">
|
||||
Recurring Document is automatically created.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="container">
|
||||
<div class="row">
|
||||
<h2 class="oe_slogan">Recurring Type</h2>
|
||||
<div class="col-md-6">
|
||||
<p class="oe_mt8 text-justify">Recurring Type is automatically
|
||||
created</p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<img class="oe_picture oe_screenshot" src="rec_type.png">
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="container">
|
||||
<div class="row">
|
||||
<h2 class="oe_slogan">Scheduled Actions</h2>
|
||||
<div class="col-md-6">
|
||||
<img class="oe_picture oe_screenshot" src="cron_job.png">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<p class="oe_mt8 text-justify">Cron job is automatically
|
||||
created</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
BIN
addons/recurring_sale/static/description/rec_doc.png
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
addons/recurring_sale/static/description/rec_type.png
Normal file
After Width: | Height: | Size: 20 KiB |
After Width: | Height: | Size: 34 KiB |
BIN
addons/recurring_sale/static/description/recurring_documents.png
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
addons/recurring_sale/static/description/sale_order.png
Normal file
After Width: | Height: | Size: 58 KiB |
3
addons/recurring_sale/tests/__init__.py
Normal file
@ -0,0 +1,3 @@
|
||||
# Part of Flectra. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import test_sale_recurring
|
20
addons/recurring_sale/tests/test_sale_recurring.py
Normal file
@ -0,0 +1,20 @@
|
||||
# Part of Flectra. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from flectra.tests.common import TransactionCase
|
||||
|
||||
|
||||
class RecurringTransactionCase(TransactionCase):
|
||||
|
||||
def setUp(self):
|
||||
super(RecurringTransactionCase, self).setUp()
|
||||
self.sale_order = self.env.ref("sale.sale_order_2")
|
||||
|
||||
def create_sale_recurring(self):
|
||||
recurring_id = self.env['recurring'].create({
|
||||
'name': 'SO0002',
|
||||
'exec_init': 5,
|
||||
})
|
||||
recurring_id.btn_recurring()
|
||||
|
||||
def test_01_recurring(self):
|
||||
self.create_sale_recurring()
|
33
addons/recurring_sale/views/sale_views.xml
Normal file
@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<flectra>
|
||||
|
||||
<record id="view_order_form_inherit" model="ir.ui.view">
|
||||
<field name="name">sale.order.form.inherit</field>
|
||||
<field name="model">sale.order</field>
|
||||
<field name="inherit_id" ref="sale.view_order_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='payment_term_id']" position="after">
|
||||
<field name="recurring_id" invisible="1"/>
|
||||
<field name="rec_source_id" readonly="1"/>
|
||||
</xpath>
|
||||
<xpath expr="//button[@name='action_cancel']" position="before">
|
||||
<button name="%(recurring.action_recurring_wizard)d"
|
||||
id="generate_recurring"
|
||||
string="Generate Recurring" class="btn-primary"
|
||||
type="action"
|
||||
attrs="{'invisible': ['|', ('state', 'in', ['draft','cancel']), ('recurring_id', '!=', False)]}"/>
|
||||
</xpath>
|
||||
<xpath expr="//sheet/div[@name='button_box']" position="inside">
|
||||
<button class="oe_stat_button" type="object"
|
||||
string="Recurring" name="get_recurring"
|
||||
icon="fa-tasks" attrs="{'invisible': [('recurring_id', '=', False)]}"/>
|
||||
|
||||
<button class="oe_stat_button" type="object"
|
||||
string="Document" name="get_recurring_documents"
|
||||
icon="fa-tasks" attrs="{'invisible': [('recurring_id', '=', False)]}"/>
|
||||
</xpath>
|
||||
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</flectra>
|