[ADD][11.0] account_spread_contract

This commit is contained in:
Andrea
2018-11-29 13:45:06 +01:00
parent 88ec79aeef
commit 920a4464a7
20 changed files with 1063 additions and 0 deletions

View File

@@ -0,0 +1,117 @@
============================
Cost-Revenue Spread Contract
============================
.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png
:target: https://odoo-community.org/page/development-status
:alt: Alpha
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Faccount--financial--tools-lightgray.png?logo=github
:target: https://github.com/OCA/account-financial-tools/tree/11.0/account_spread_contract
:alt: OCA/account-financial-tools
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/account-financial-tools-11-0/account-financial-tools-11-0-account_spread_contract
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
:target: https://runbot.odoo-community.org/runbot/92/11.0
:alt: Try me on Runbot
|badge1| |badge2| |badge3| |badge4| |badge5|
This module allows to spread costs or revenues over a period defined in a contract.
When creating recurring invoices, the cost/revenue spread functionality is enabled on the invoice lines.
The costs/revenues spread functionality is provided by module *account_spread_cost_revenue*.
**Table of contents**
.. contents::
:local:
Configuration
=============
Under Invoicing -> Configuration -> Accounting -> Spread Templates, create a new spread template.
* *Spread Type*
* *Spread Balance Sheet Account*
* *Journal*
Usage
=====
Create a contract that will generate recurring invoices.
On its lines, the spreading right-arrow icon are displayed in dark-grey color.
Click on the spreading right-arrow icon. A wizard prompts to enter a *Spread Template*.
Click on Confirm button: the selected Spread Template will be automatically displayed.
Go back to the contract. The spreading functionality is now enabled on the contract line:
the spreading right-arrow icon is now displayed in green color.
When generating an invoice from the contract, the spread table will be automatically created
and linked to the respective invoice line.
Changelog
=========
11.0.1.0.0
~~~~~~~~~~
* [ADD] Module account_spread_contract.
Bug Tracker
===========
Bugs are tracked on `GitHub Issues <https://github.com/OCA/account-financial-tools/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed
`feedback <https://github.com/OCA/account-financial-tools/issues/new?body=module:%20account_spread_contract%0Aversion:%2011.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
Do not contact contributors directly about support or help with technical issues.
Credits
=======
Authors
~~~~~~~
* Onestein
Contributors
~~~~~~~~~~~~
* Andrea Stirpe <a.stirpe@onestein.nl>
Maintainers
~~~~~~~~~~~
This module is maintained by the OCA.
.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org
OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.
.. |maintainer-astirpe| image:: https://github.com/astirpe.png?size=40px
:target: https://github.com/astirpe
:alt: astirpe
Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:
|maintainer-astirpe|
This module is part of the `OCA/account-financial-tools <https://github.com/OCA/account-financial-tools/tree/11.0/account_spread_contract>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

View File

@@ -0,0 +1,4 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from . import models
from . import wizards

View File

@@ -0,0 +1,24 @@
# Copyright 2018-2019 Onestein (<https://www.onestein.eu>)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
{
"name": "Cost-Revenue Spread Contract",
"summary": "Spread costs and revenues from contracts",
"version": "11.0.1.0.0",
"development_status": "Beta",
"author": "Onestein,Odoo Community Association (OCA)",
"maintainers": ["astirpe"],
"license": "AGPL-3",
"website": "https://github.com/OCA/account-financial-tools/",
"category": "Accounting & Finance",
"depends": [
"account_spread_cost_revenue",
"contract",
],
"data": [
"views/account_analytic_account.xml",
"views/account_spread_template.xml",
"wizards/account_spread_contract_line_link_wizard.xml",
],
"installable": True,
}

View File

@@ -0,0 +1,5 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from . import account_analytic_account
from . import account_analytic_invoice_line
from . import account_spread_template

View File

@@ -0,0 +1,110 @@
# Copyright 2018-2019 Onestein (<https://www.onestein.eu>)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import _, api, models
from odoo.exceptions import UserError
class AccountAnalyticAccount(models.Model):
_inherit = 'account.analytic.account'
@api.multi
def _create_invoice(self, invoice=False):
"""
:param invoice: If not False add lines to this invoice
:return: invoice created or updated
"""
self.ensure_one()
line_template_map = {}
for line in self.recurring_invoice_line_ids:
if invoice and invoice.state == 'draft':
pass
else:
self._create_spread_by_template_map(line, line_template_map)
ctx = dict(
self.env.context,
contract_spread_template_map=line_template_map,
)
res = super(
AccountAnalyticAccount,
self.with_context(ctx)
)._create_invoice(invoice)
for line in self.recurring_invoice_line_ids:
if line.id in line_template_map:
spread_id = line_template_map[line.id]
spread = self.env['account.spread'].browse(spread_id)
spread.compute_spread_board()
return res
def _create_spread_by_template_map(self, line, line_template_map):
self.ensure_one()
if line.spread_template_id:
new_spread_vals = self._prepare_spread_vals(line)
new_spread = self.env['account.spread'].create(new_spread_vals)
line_template_map[line.id] = new_spread.id
def _prepare_spread_vals(self, line):
self.ensure_one()
template = line.spread_template_id
new_spread_vals = template._prepare_spread_from_template()
invoice_type = 'out_invoice'
if self.contract_type == 'purchase':
invoice_type = 'in_invoice'
fpos_id = self._prepare_invoice().get('fiscal_position_id')
fiscal_position = self.env['account.fiscal.position'].browse(fpos_id)
account = self.env['account.invoice.line'].get_invoice_line_account(
invoice_type,
line.product_id,
fiscal_position,
self.company_id
)
if not account:
raise UserError(_("Unable to get the Invoice Line Account."))
if self.contract_type == 'purchase':
new_spread_vals['debit_account_id'] = account.id
else:
new_spread_vals['credit_account_id'] = account.id
lang = self.env['res.lang'].search(
[('code', '=', self.partner_id.lang)])
date_format = lang.date_format or '%m/%d/%Y'
invoice_line_name = self._insert_markers(line, date_format)
spread_name = _('Contract %s (%s) %s') % (
self.name,
self.recurring_next_date,
invoice_line_name
)
analytic_tags = [(4, tag.id, None) for tag in self.tag_ids]
new_spread_vals.update({
'name': spread_name,
'period_number': self.recurring_interval,
'period_type': self._get_spread_period_type(),
'spread_date': self.recurring_next_date,
'account_analytic_id': self.id,
'analytic_tag_ids': analytic_tags,
})
return new_spread_vals
def _get_spread_period_type(self):
self.ensure_one()
recurring_rule_type = self.recurring_rule_type
if recurring_rule_type not in ['monthly', 'monthlylastday', 'yearly']:
raise UserError(
_("Not implemented."))
if recurring_rule_type == 'yearly':
return 'year'
return 'month'
@api.model
def _prepare_invoice_line(self, line, invoice_id):
template_map = self.env.context.get('contract_spread_template_map')
res = super()._prepare_invoice_line(line, invoice_id)
if line.id in template_map:
spread_id = template_map[line.id]
res['spread_id'] = spread_id
return res

View File

@@ -0,0 +1,64 @@
# Copyright 2018-2019 Onestein (<https://www.onestein.eu>)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import _, api, fields, models
class AccountAnalyticInvoiceLine(models.Model):
_inherit = 'account.analytic.invoice.line'
spread_template_id = fields.Many2one(
'account.spread.template',
string='Spread Template',
copy=False)
spread_check = fields.Selection([
('linked', 'Linked'),
('unlinked', 'Unlinked'),
('unavailable', 'Unavailable')
], compute='_compute_spread_check')
@api.depends('spread_template_id')
def _compute_spread_check(self):
for line in self:
if line.spread_template_id:
line.spread_check = 'linked'
elif not line.analytic_account_id.create_invoice_visibility:
line.spread_check = 'unavailable'
else:
line.spread_check = 'unlinked'
@api.multi
def spread_details(self):
"""Button on the contract lines tree view of the contract
form to show the spread template form view."""
if not self:
# In case the widget clicked before the creation of the line
return
if self.spread_template_id:
return {
'name': _('Spread Template Details'),
'view_type': 'form',
'view_mode': 'form',
'res_model': 'account.spread.template',
'type': 'ir.actions.act_window',
'target': 'current',
'readonly': False,
'res_id': self.spread_template_id.id,
'context': {'force_contract_line_id': self.id},
}
ctx = dict(
self.env.context,
default_contract_line_id=self.id,
default_company_id=self.analytic_account_id.company_id.id,
)
return {
'name': _('Link Contract Line with Spread Template'),
'view_type': 'form',
'view_mode': 'form',
'res_model': 'account.spread.contract.line.link.wizard',
'type': 'ir.actions.act_window',
'target': 'new',
'context': ctx,
}

View File

@@ -0,0 +1,38 @@
# Copyright 2018-2019 Onestein (<https://www.onestein.eu>)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import _, api, fields, models
from odoo.exceptions import ValidationError
class AccountSpreadTemplate(models.Model):
_inherit = 'account.spread.template'
contract_line_ids = fields.One2many(
'account.analytic.invoice.line',
'spread_template_id',
string='Contract Lines',
)
@api.constrains('contract_line_ids', 'spread_type')
def _check_spread_template_contract_type(self):
for template in self:
for line in template.contract_line_ids:
contract = line.analytic_account_id.contract_template_id
contract_type = contract.contract_type
if contract_type == 'sale' or not contract_type:
if template.spread_type != 'sale':
raise ValidationError(_(
'The contract type (Sales) is not compatible '
'with selected Template Spread Type'))
elif contract_type == 'purchase':
if template.spread_type != 'purchase':
raise ValidationError(_(
'The contract type (Purchases) is not compatible '
'with selected Template Spread Type'))
@api.multi
def action_unlink_contract_line(self):
line_id = self.env.context.get('force_contract_line_id')
line = self.env['account.analytic.invoice.line'].browse(line_id)
line.spread_template_id = False

View File

@@ -0,0 +1,5 @@
Under Invoicing -> Configuration -> Accounting -> Spread Templates, create a new spread template.
* *Spread Type*
* *Spread Balance Sheet Account*
* *Journal*

View File

@@ -0,0 +1 @@
* Andrea Stirpe <a.stirpe@onestein.nl>

View File

@@ -0,0 +1,4 @@
This module allows to spread costs or revenues over a period defined in a contract.
When creating recurring invoices, the cost/revenue spread functionality is enabled on the invoice lines.
The costs/revenues spread functionality is provided by module *account_spread_cost_revenue*.

View File

@@ -0,0 +1,4 @@
11.0.1.0.0
~~~~~~~~~~
* [ADD] Module account_spread_contract.

View File

@@ -0,0 +1,15 @@
Create a contract that will generate recurring invoices.
On its lines, the spreading right-arrow icon are displayed in dark-grey color.
Click on the spreading right-arrow icon. A wizard prompts to enter a *Spread Template*.
Click on Confirm button: the selected Spread Template will be automatically displayed.
Go back to the contract. The spreading functionality is now enabled on the contract line:
the spreading right-arrow icon is now displayed in green color.
When generating an invoice from the contract, the spread table will be automatically created
and linked to the respective invoice line.
The spread date is the date from the next invoice date present in the contract.
The period of the contract is copied to the spread. So if the contract is 3 months, it will also set
the spread period to 3x1 months.

View File

@@ -0,0 +1,458 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils 0.14: http://docutils.sourceforge.net/" />
<title>Cost-Revenue Spread Contract</title>
<style type="text/css">
/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 7952 2016-07-26 18:15:59Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
customize this style sheet.
*/
/* used to remove borders from tables and images */
.borderless, table.borderless td, table.borderless th {
border: 0 }
table.borderless td, table.borderless th {
/* Override padding for "table.docutils td" with "! important".
The right padding separates the table cells. */
padding: 0 0.5em 0 0 ! important }
.first {
/* Override more specific margin styles with "! important". */
margin-top: 0 ! important }
.last, .with-subtitle {
margin-bottom: 0 ! important }
.hidden {
display: none }
.subscript {
vertical-align: sub;
font-size: smaller }
.superscript {
vertical-align: super;
font-size: smaller }
a.toc-backref {
text-decoration: none ;
color: black }
blockquote.epigraph {
margin: 2em 5em ; }
dl.docutils dd {
margin-bottom: 0.5em }
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
overflow: hidden;
}
/* Uncomment (and remove this text!) to get bold-faced definition list terms
dl.docutils dt {
font-weight: bold }
*/
div.abstract {
margin: 2em 5em }
div.abstract p.topic-title {
font-weight: bold ;
text-align: center }
div.admonition, div.attention, div.caution, div.danger, div.error,
div.hint, div.important, div.note, div.tip, div.warning {
margin: 2em ;
border: medium outset ;
padding: 1em }
div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title {
font-weight: bold ;
font-family: sans-serif }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title, .code .error {
color: red ;
font-weight: bold ;
font-family: sans-serif }
/* Uncomment (and remove this text!) to get reduced vertical space in
compound paragraphs.
div.compound .compound-first, div.compound .compound-middle {
margin-bottom: 0.5em }
div.compound .compound-last, div.compound .compound-middle {
margin-top: 0.5em }
*/
div.dedication {
margin: 2em 5em ;
text-align: center ;
font-style: italic }
div.dedication p.topic-title {
font-weight: bold ;
font-style: normal }
div.figure {
margin-left: 2em ;
margin-right: 2em }
div.footer, div.header {
clear: both;
font-size: smaller }
div.line-block {
display: block ;
margin-top: 1em ;
margin-bottom: 1em }
div.line-block div.line-block {
margin-top: 0 ;
margin-bottom: 0 ;
margin-left: 1.5em }
div.sidebar {
margin: 0 0 0.5em 1em ;
border: medium outset ;
padding: 1em ;
background-color: #ffffee ;
width: 40% ;
float: right ;
clear: right }
div.sidebar p.rubric {
font-family: sans-serif ;
font-size: medium }
div.system-messages {
margin: 5em }
div.system-messages h1 {
color: red }
div.system-message {
border: medium outset ;
padding: 1em }
div.system-message p.system-message-title {
color: red ;
font-weight: bold }
div.topic {
margin: 2em }
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
margin-top: 0.4em }
h1.title {
text-align: center }
h2.subtitle {
text-align: center }
hr.docutils {
width: 75% }
img.align-left, .figure.align-left, object.align-left, table.align-left {
clear: left ;
float: left ;
margin-right: 1em }
img.align-right, .figure.align-right, object.align-right, table.align-right {
clear: right ;
float: right ;
margin-left: 1em }
img.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
table.align-center {
margin-left: auto;
margin-right: auto;
}
.align-left {
text-align: left }
.align-center {
clear: both ;
text-align: center }
.align-right {
text-align: right }
/* reset inner alignment in figures */
div.align-right {
text-align: inherit }
/* div.align-center * { */
/* text-align: left } */
.align-top {
vertical-align: top }
.align-middle {
vertical-align: middle }
.align-bottom {
vertical-align: bottom }
ol.simple, ul.simple {
margin-bottom: 1em }
ol.arabic {
list-style: decimal }
ol.loweralpha {
list-style: lower-alpha }
ol.upperalpha {
list-style: upper-alpha }
ol.lowerroman {
list-style: lower-roman }
ol.upperroman {
list-style: upper-roman }
p.attribution {
text-align: right ;
margin-left: 50% }
p.caption {
font-style: italic }
p.credits {
font-style: italic ;
font-size: smaller }
p.label {
white-space: nowrap }
p.rubric {
font-weight: bold ;
font-size: larger ;
color: maroon ;
text-align: center }
p.sidebar-title {
font-family: sans-serif ;
font-weight: bold ;
font-size: larger }
p.sidebar-subtitle {
font-family: sans-serif ;
font-weight: bold }
p.topic-title {
font-weight: bold }
pre.address {
margin-bottom: 0 ;
margin-top: 0 ;
font: inherit }
pre.literal-block, pre.doctest-block, pre.math, pre.code {
margin-left: 2em ;
margin-right: 2em }
pre.code .ln { color: grey; } /* line numbers */
pre.code, code { background-color: #eeeeee }
pre.code .comment, code .comment { color: #5C6576 }
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
pre.code .literal.string, code .literal.string { color: #0C5404 }
pre.code .name.builtin, code .name.builtin { color: #352B84 }
pre.code .deleted, code .deleted { background-color: #DEB0A1}
pre.code .inserted, code .inserted { background-color: #A3D289}
span.classifier {
font-family: sans-serif ;
font-style: oblique }
span.classifier-delimiter {
font-family: sans-serif ;
font-weight: bold }
span.interpreted {
font-family: sans-serif }
span.option {
white-space: nowrap }
span.pre {
white-space: pre }
span.problematic {
color: red }
span.section-subtitle {
/* font-size relative to parent (h1..h6 element) */
font-size: 80% }
table.citation {
border-left: solid 1px gray;
margin-left: 1px }
table.docinfo {
margin: 2em 4em }
table.docutils {
margin-top: 0.5em ;
margin-bottom: 0.5em }
table.footnote {
border-left: solid 1px black;
margin-left: 1px }
table.docutils td, table.docutils th,
table.docinfo td, table.docinfo th {
padding-left: 0.5em ;
padding-right: 0.5em ;
vertical-align: top }
table.docutils th.field-name, table.docinfo th.docinfo-name {
font-weight: bold ;
text-align: left ;
white-space: nowrap ;
padding-left: 0 }
/* "booktabs" style (no vertical lines) */
table.docutils.booktabs {
border: 0px;
border-top: 2px solid;
border-bottom: 2px solid;
border-collapse: collapse;
}
table.docutils.booktabs * {
border: 0px;
}
table.docutils.booktabs th {
border-bottom: thin solid;
text-align: left;
}
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
font-size: 100% }
ul.auto-toc {
list-style-type: none }
</style>
</head>
<body>
<div class="document" id="cost-revenue-spread-contract">
<h1 class="title">Cost-Revenue Spread Contract</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external" href="https://odoo-community.org/page/development-status"><img alt="Alpha" src="https://img.shields.io/badge/maturity-Alpha-red.png" /></a> <a class="reference external" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external" href="https://github.com/OCA/account-financial-tools/tree/11.0/account_spread_contract"><img alt="OCA/account-financial-tools" src="https://img.shields.io/badge/github-OCA%2Faccount--financial--tools-lightgray.png?logo=github" /></a> <a class="reference external" href="https://translation.odoo-community.org/projects/account-financial-tools-11-0/account-financial-tools-11-0-account_spread_contract"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external" href="https://runbot.odoo-community.org/runbot/92/11.0"><img alt="Try me on Runbot" src="https://img.shields.io/badge/runbot-Try%20me-875A7B.png" /></a></p>
<p>This module allows to spread costs or revenues over a period defined in a contract.</p>
<p>When creating recurring invoices, the cost/revenue spread functionality is enabled on the invoice lines.
The costs/revenues spread functionality is provided by module <em>account_spread_cost_revenue</em>.</p>
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">
<ul class="simple">
<li><a class="reference internal" href="#configuration" id="id2">Configuration</a></li>
<li><a class="reference internal" href="#usage" id="id3">Usage</a></li>
<li><a class="reference internal" href="#changelog" id="id4">Changelog</a><ul>
<li><a class="reference internal" href="#id1" id="id5">11.0.1.0.0</a></li>
</ul>
</li>
<li><a class="reference internal" href="#bug-tracker" id="id6">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="id7">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="id8">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="id9">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="id10">Maintainers</a></li>
</ul>
</li>
</ul>
</div>
<div class="section" id="configuration">
<h1><a class="toc-backref" href="#id2">Configuration</a></h1>
<p>Under Invoicing -&gt; Configuration -&gt; Accounting -&gt; Spread Templates, create a new spread template.</p>
<ul class="simple">
<li><em>Spread Type</em></li>
<li><em>Spread Balance Sheet Account</em></li>
<li><em>Journal</em></li>
</ul>
</div>
<div class="section" id="usage">
<h1><a class="toc-backref" href="#id3">Usage</a></h1>
<p>Create a contract that will generate recurring invoices.
On its lines, the spreading right-arrow icon are displayed in dark-grey color.</p>
<p>Click on the spreading right-arrow icon. A wizard prompts to enter a <em>Spread Template</em>.</p>
<p>Click on Confirm button: the selected Spread Template will be automatically displayed.</p>
<p>Go back to the contract. The spreading functionality is now enabled on the contract line:
the spreading right-arrow icon is now displayed in green color.</p>
<p>When generating an invoice from the contract, the spread table will be automatically created
and linked to the respective invoice line.</p>
</div>
<div class="section" id="changelog">
<h1><a class="toc-backref" href="#id4">Changelog</a></h1>
<div class="section" id="id1">
<h2><a class="toc-backref" href="#id5">11.0.1.0.0</a></h2>
<ul class="simple">
<li>[ADD] Module account_spread_contract.</li>
</ul>
</div>
</div>
<div class="section" id="bug-tracker">
<h1><a class="toc-backref" href="#id6">Bug Tracker</a></h1>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/account-financial-tools/issues">GitHub Issues</a>.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed
<a class="reference external" href="https://github.com/OCA/account-financial-tools/issues/new?body=module:%20account_spread_contract%0Aversion:%2011.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<p>Do not contact contributors directly about support or help with technical issues.</p>
</div>
<div class="section" id="credits">
<h1><a class="toc-backref" href="#id7">Credits</a></h1>
<div class="section" id="authors">
<h2><a class="toc-backref" href="#id8">Authors</a></h2>
<ul class="simple">
<li>Onestein</li>
</ul>
</div>
<div class="section" id="contributors">
<h2><a class="toc-backref" href="#id9">Contributors</a></h2>
<ul class="simple">
<li>Andrea Stirpe &lt;<a class="reference external" href="mailto:a.stirpe&#64;onestein.nl">a.stirpe&#64;onestein.nl</a>&gt;</li>
</ul>
</div>
<div class="section" id="maintainers">
<h2><a class="toc-backref" href="#id10">Maintainers</a></h2>
<p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org"><img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" /></a>
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.</p>
<p>Current <a class="reference external" href="https://odoo-community.org/page/maintainer-role">maintainer</a>:</p>
<p><a class="reference external" href="https://github.com/astirpe"><img alt="astirpe" src="https://github.com/astirpe.png?size=40px" /></a></p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/account-financial-tools/tree/11.0/account_spread_contract">OCA/account-financial-tools</a> project on GitHub.</p>
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
</div>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,3 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from . import test_account_spread_contract

View File

@@ -0,0 +1,105 @@
# Copyright 2018-2019 Onestein (<https://www.onestein.eu>)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo.tools import convert_file
from odoo.modules.module import get_module_resource
from odoo.addons.contract.tests.test_contract import TestContractBase
class TestAccountSpreadContract(TestContractBase):
def _load(self, module, *args):
convert_file(
self.cr,
'account_spread_contract',
get_module_resource(module, *args),
{}, 'init', False, 'test', self.registry._assertion_report)
def setUp(self):
super().setUp()
self._load('account', 'test', 'account_minimal_test.xml')
self.contract.recurring_next_date = '2016-02-29'
self.contract.recurring_invoicing_type = 'pre-paid'
self.contract.recurring_rule_type = 'monthly'
self.receivable_account = self.env['account.account'].search([(
'user_type_id',
'=',
self.env.ref('account.data_account_type_receivable').id)],
limit=1)
self.sales_journal_journal_id = self.ref(
'account_spread_contract.sales_journal')
self.sale_template = self.env['account.spread.template'].create({
'name': 'test',
'spread_type': 'sale',
'spread_account_id': self.receivable_account.id,
'spread_journal_id': self.sales_journal_journal_id,
})
def test_01_create_recurring_invoice_with_spread(self):
self.assertTrue(self.receivable_account)
self.assertEqual(len(self.contract.recurring_invoice_line_ids), 1)
contract_line = self.contract.recurring_invoice_line_ids
self.assertEqual(contract_line.spread_check, 'unlinked')
contract_line.spread_template_id = self.sale_template
self.assertEqual(contract_line.spread_check, 'linked')
self.contract.recurring_create_invoice()
invoice_monthly = self.env['account.invoice'].search(
[('contract_id', '=', self.contract.id)])
self.assertEqual(len(invoice_monthly), 1)
self.assertEqual(len(invoice_monthly.invoice_line_ids), 1)
spread = invoice_monthly.invoice_line_ids.spread_id
self.assertTrue(spread)
self.assertEqual(spread.template_id, self.sale_template)
self.assertEqual(contract_line.spread_template_id, self.sale_template)
def test_02_open_wizard(self):
contract_line = self.contract.recurring_invoice_line_ids
res_action = contract_line.spread_details()
self.assertTrue(isinstance(res_action, dict))
self.assertFalse(res_action.get('res_id'))
self.assertTrue(res_action.get('context'))
contract_line.spread_template_id = self.sale_template
res_action = contract_line.spread_details()
self.assertTrue(isinstance(res_action, dict))
self.assertTrue(res_action.get('res_id'))
self.assertTrue(res_action.get('context'))
def test_03_wizard_create(self):
my_company = self.env.user.company_id
contract_line = self.contract.recurring_invoice_line_ids
self.assertFalse(contract_line.spread_template_id)
Wizard = self.env['account.spread.contract.line.link.wizard']
wizard = Wizard.with_context(
default_contract_line_id=contract_line.id,
default_company_id=my_company.id,
).create({
'spread_template_id': self.sale_template.id,
})
self.assertEqual(wizard.contract_line_id, contract_line)
self.assertEqual(wizard.contract_id, self.contract)
self.assertEqual(wizard.contract_type, 'sale')
self.assertEqual(wizard.spread_template_id, self.sale_template)
self.assertEqual(wizard.company_id, my_company)
self.assertEqual(contract_line.spread_check, 'unlinked')
wizard.confirm()
self.assertEqual(contract_line.spread_template_id, self.sale_template)
self.assertEqual(contract_line.spread_check, 'linked')
ctx = {'force_contract_line_id': contract_line.id}
self.sale_template.with_context(ctx).action_unlink_contract_line()
self.assertFalse(contract_line.spread_template_id)
self.assertEqual(contract_line.spread_check, 'unlinked')

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="account_analytic_account_recurring_form_form" model="ir.ui.view">
<field name="model">account.analytic.account</field>
<field name="inherit_id" ref="contract.account_analytic_account_recurring_form_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='recurring_invoice_line_ids']/tree/field[@name='uom_id']" position="after">
<field name="spread_check" widget="spread_line_widget" groups="account.group_account_user,account.group_account_manager"/>
</xpath>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_account_spread_template" model="ir.ui.view">
<field name="model">account.spread.template</field>
<field name="inherit_id" ref="account_spread_cost_revenue.view_account_spread_template"/>
<field name="arch" type="xml">
<header position="inside">
<button name="action_unlink_contract_line" type="object" string="Unlink Contract Line" invisible="not context.get('force_contract_line_id')" groups="account.group_account_manager"/>
</header>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,3 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from . import account_spread_contract_line_link_wizard

View File

@@ -0,0 +1,48 @@
# Copyright 2018-2019 Onestein (<https://www.onestein.eu>)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import _, api, fields, models
class AccountSpreadContractLineLinkWizard(models.TransientModel):
_name = 'account.spread.contract.line.link.wizard'
_description = 'Account Spread Contract Line Link Wizard'
contract_line_id = fields.Many2one(
'account.analytic.invoice.line',
string='Contract Line',
readonly=True,
required=True)
contract_id = fields.Many2one(
related='contract_line_id.analytic_account_id',
readonly=True)
contract_type = fields.Selection(
related='contract_line_id.analytic_account_id.contract_type',
readonly=True)
spread_template_id = fields.Many2one(
'account.spread.template',
string='Spread Template',
required=True)
company_id = fields.Many2one(
'res.company',
string='Company',
required=True)
@api.multi
def confirm(self):
self.ensure_one()
if not self.contract_line_id.spread_template_id:
self.contract_line_id.spread_template_id = self.spread_template_id
if self.spread_template_id:
return {
'name': _('Spread Template Details'),
'view_type': 'form',
'view_mode': 'form',
'res_model': 'account.spread.template',
'type': 'ir.actions.act_window',
'target': 'current',
'readonly': False,
'res_id': self.spread_template_id.id,
}

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_account_spread_contract_line_link_wizard" model="ir.ui.view">
<field name="model">account.spread.contract.line.link.wizard</field>
<field name="arch" type="xml">
<form>
<group name="main_info">
<group>
<field name="company_id" readonly="1" groups="base.group_multi_company"/>
<field name="contract_type" readonly="1"/>
<field name="contract_id" readonly="1"/>
<field name="contract_line_id" readonly="1"/>
</group>
<group>
<field name="spread_template_id" domain="[('spread_type', '=', contract_type)]"/>
</group>
</group>
<footer>
<button string="Confirm" type="object" name="confirm" class="btn-primary"/>
<button string="Cancel" class="oe_link" special="cancel" />
</footer>
</form>
</field>
</record>
</odoo>