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