mirror of
https://github.com/OCA/contract.git
synced 2025-02-13 17:57:24 +02:00
[ADD] contract_variable_quantity:
================================================= Variable quantity in contract recurrent invoicing ================================================= With this module, you will be able to define in recurring contracts some lines with variable quantity according a provided formula. Configuration ============= * Go to Sales > Configuration > Contracts > Formulas (quantity). * Define any formula based on Python code that stores at some moment a float/integer value of the quantity to invoice in the variable 'result'. You can use these variables to compute your formula: * *env*: Environment variable for getting other models. * *context*: Current context dictionary. * *user*: Current user. * *line*: Contract recurring invoice line that triggers this formula. * *contract*: Contract whose line belongs to. * *invoice*: Invoice (header) being created. Usage ===== To use this module, you need to: * Go to Sales -> Contracts and select or create a new contract. * Check *Generate recurring invoices automatically*. * Add a new recurring invoicing line. * Select "Variable quantity" in column "Qty. type". * Select one of the possible formulas to use (previously created).
This commit is contained in:
committed by
Víctor Martínez
parent
929495172c
commit
222dc7373e
72
contract_variable_quantity/README.rst
Normal file
72
contract_variable_quantity/README.rst
Normal file
@@ -0,0 +1,72 @@
|
||||
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
|
||||
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
|
||||
:alt: License: AGPL-3
|
||||
|
||||
=================================================
|
||||
Variable quantity in contract recurrent invoicing
|
||||
=================================================
|
||||
|
||||
With this module, you will be able to define in recurring contracts some
|
||||
lines with variable quantity according a provided formula.
|
||||
|
||||
Configuration
|
||||
=============
|
||||
|
||||
#. Go to Sales > Configuration > Contracts > Formulas (quantity).
|
||||
#. Define any formula based on Python code that stores at some moment a
|
||||
float/integer value of the quantity to invoice in the variable 'result'.
|
||||
|
||||
You can use these variables to compute your formula:
|
||||
|
||||
* *env*: Environment variable for getting other models.
|
||||
* *context*: Current context dictionary.
|
||||
* *user*: Current user.
|
||||
* *line*: Contract recurring invoice line that triggers this formula.
|
||||
* *contract*: Contract whose line belongs to.
|
||||
* *invoice*: Invoice (header) being created.
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
To use this module, you need to:
|
||||
|
||||
#. Go to Sales -> Contracts and select or create a new contract.
|
||||
#. Check *Generate recurring invoices automatically*.
|
||||
#. Add a new recurring invoicing line.
|
||||
#. Select "Variable quantity" in column "Qty. type".
|
||||
#. Select one of the possible formulas to use (previously created).
|
||||
|
||||
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
|
||||
:alt: Try me on Runbot
|
||||
:target: https://runbot.odoo-community.org/runbot/110/9.0
|
||||
|
||||
Bug Tracker
|
||||
===========
|
||||
|
||||
Bugs are tracked on `GitHub Issues
|
||||
<https://github.com/OCA/contract/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.
|
||||
|
||||
Credits
|
||||
=======
|
||||
|
||||
Contributors
|
||||
------------
|
||||
|
||||
* Pedro M. Baeza <pedro.baeza@tecnativa.com>
|
||||
|
||||
Maintainer
|
||||
----------
|
||||
|
||||
.. image:: https://odoo-community.org/logo.png
|
||||
:alt: Odoo Community Association
|
||||
:target: https://odoo-community.org
|
||||
|
||||
This module is maintained by the OCA.
|
||||
|
||||
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.
|
||||
|
||||
To contribute to this module, please visit https://odoo-community.org.
|
||||
4
contract_variable_quantity/__init__.py
Normal file
4
contract_variable_quantity/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from . import models
|
||||
21
contract_variable_quantity/__openerp__.py
Normal file
21
contract_variable_quantity/__openerp__.py
Normal file
@@ -0,0 +1,21 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# © 2016 Pedro M. Baeza <pedro.baeza@tecnativa.com>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
{
|
||||
'name': 'Variable quantity in contract recurrent invoicing',
|
||||
'version': '9.0.1.0.0',
|
||||
'category': 'Contract Management',
|
||||
'license': 'AGPL-3',
|
||||
'author': "Tecnativa,"
|
||||
"Odoo Community Association (OCA)",
|
||||
'website': 'https://www.tecnativa.com',
|
||||
'depends': [
|
||||
'contract',
|
||||
],
|
||||
'data': [
|
||||
'security/ir.model.access.csv',
|
||||
'views/contract_view.xml',
|
||||
],
|
||||
'installable': True,
|
||||
}
|
||||
4
contract_variable_quantity/models/__init__.py
Normal file
4
contract_variable_quantity/models/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from . import contract
|
||||
66
contract_variable_quantity/models/contract.py
Normal file
66
contract_variable_quantity/models/contract.py
Normal file
@@ -0,0 +1,66 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# © 2016 Pedro M. Baeza <pedro.baeza@tecnativa.com>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from openerp import _, api, fields, models, exceptions
|
||||
from openerp.tools.safe_eval import safe_eval
|
||||
|
||||
|
||||
class AccountAnalyticAccount(models.Model):
|
||||
_inherit = "account.analytic.account"
|
||||
|
||||
@api.model
|
||||
def _prepare_invoice_line(self, line, invoice_id):
|
||||
vals = super(AccountAnalyticAccount, self)._prepare_invoice_line(
|
||||
line, invoice_id)
|
||||
if line.qty_type == 'variable':
|
||||
eval_context = {
|
||||
'env': self.env,
|
||||
'context': self.env.context,
|
||||
'user': self.env.user,
|
||||
'line': line,
|
||||
'contract': line.analytic_account_id,
|
||||
'invoice': self.env['account.invoice'].browse(invoice_id),
|
||||
}
|
||||
safe_eval(line.qty_formula_id.code.strip(), eval_context,
|
||||
mode="exec", nocopy=True) # nocopy for returning result
|
||||
vals['quantity'] = eval_context.get('result', 0)
|
||||
return vals
|
||||
|
||||
|
||||
class AccountAnalyticInvoiceLine(models.Model):
|
||||
_inherit = 'account.analytic.invoice.line'
|
||||
|
||||
qty_type = fields.Selection(
|
||||
selection=[
|
||||
('fixed', 'Fixed quantity'),
|
||||
('variable', 'Variable quantity'),
|
||||
], required=True, default='fixed', string="Qty. type")
|
||||
qty_formula_id = fields.Many2one(
|
||||
comodel_name="contract.line.qty.formula", string="Qty. formula")
|
||||
|
||||
|
||||
class ContractLineFormula(models.Model):
|
||||
_name = 'contract.line.qty.formula'
|
||||
|
||||
name = fields.Char(required=True)
|
||||
code = fields.Text(required=True, default="result = 0")
|
||||
|
||||
@api.constrains('code')
|
||||
def _check_code(self):
|
||||
eval_context = {
|
||||
'env': self.env,
|
||||
'context': self.env.context,
|
||||
'user': self.env.user,
|
||||
'line': self.env['account.analytic.invoice.line'],
|
||||
'contract': self.env['account.analytic.account'],
|
||||
'invoice': self.env['account.invoice'],
|
||||
}
|
||||
try:
|
||||
safe_eval(
|
||||
self.code.strip(), eval_context, mode="exec", nocopy=True)
|
||||
except Exception as e:
|
||||
raise exceptions.ValidationError(
|
||||
_('Error evaluating code.\nDetails: %s') % e)
|
||||
if 'result' not in eval_context:
|
||||
raise exceptions.ValidationError(_('No valid result returned.'))
|
||||
3
contract_variable_quantity/security/ir.model.access.csv
Normal file
3
contract_variable_quantity/security/ir.model.access.csv
Normal file
@@ -0,0 +1,3 @@
|
||||
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
|
||||
"contract_line_qty_formula_manager","Recurring formula manager","model_contract_line_qty_formula","base.group_sale_manager",1,1,1,1
|
||||
"contract_line_qty_formula_user","Recurring formula user","model_contract_line_qty_formula","base.group_sale_salesman",1,0,0,0
|
||||
|
BIN
contract_variable_quantity/static/description/icon.png
Normal file
BIN
contract_variable_quantity/static/description/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.1 KiB |
301
contract_variable_quantity/static/description/icon.svg
Normal file
301
contract_variable_quantity/static/description/icon.svg
Normal file
@@ -0,0 +1,301 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
id="svg2"
|
||||
sodipodi:docname="icon.svg"
|
||||
viewBox="0 0 128 128"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
width="128"
|
||||
height="128"
|
||||
inkscape:export-filename="icon.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90">
|
||||
<defs
|
||||
id="defs4">
|
||||
<linearGradient
|
||||
id="linearGradient4306">
|
||||
<stop
|
||||
id="stop4308"
|
||||
style="stop-color:#dedc37"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop4310"
|
||||
style="stop-color:#f8fa8d"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3727">
|
||||
<stop
|
||||
id="stop3729"
|
||||
style="stop-color:#f3f360"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop3731"
|
||||
style="stop-color:#cbc630"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3717">
|
||||
<stop
|
||||
id="stop3719"
|
||||
style="stop-color:#e4e240"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop3725"
|
||||
style="stop-color:#d4d237"
|
||||
offset=".47047" />
|
||||
<stop
|
||||
id="stop3721"
|
||||
style="stop-color:#f8f44e"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3590">
|
||||
<stop
|
||||
id="stop3592"
|
||||
style="stop-color:#b8b852"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop3594"
|
||||
style="stop-color:#dbd649"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<filter
|
||||
inkscape:menu-tooltip="In and out glow with a possible offset and colorizable flood"
|
||||
inkscape:menu="Shadows and Glows"
|
||||
inkscape:label="Cutout Glow"
|
||||
style="color-interpolation-filters:sRGB;"
|
||||
id="filter3869">
|
||||
<feOffset
|
||||
dy="3"
|
||||
dx="3"
|
||||
id="feOffset3871" />
|
||||
<feGaussianBlur
|
||||
stdDeviation="3"
|
||||
result="blur"
|
||||
id="feGaussianBlur3873" />
|
||||
<feFlood
|
||||
flood-color="rgb(0,0,0)"
|
||||
flood-opacity="1"
|
||||
result="flood"
|
||||
id="feFlood3875" />
|
||||
<feComposite
|
||||
in="flood"
|
||||
in2="SourceGraphic"
|
||||
operator="in"
|
||||
result="composite"
|
||||
id="feComposite3877" />
|
||||
<feBlend
|
||||
in="blur"
|
||||
in2="composite"
|
||||
mode="normal"
|
||||
id="feBlend3879" />
|
||||
</filter>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
bordercolor="#666666"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-y="24"
|
||||
pagecolor="#ffffff"
|
||||
inkscape:window-height="1056"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:zoom="4.9954589"
|
||||
inkscape:window-x="65"
|
||||
showgrid="false"
|
||||
borderopacity="1.0"
|
||||
inkscape:current-layer="layer1"
|
||||
inkscape:cx="73.86699"
|
||||
inkscape:cy="46.4832"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true"
|
||||
inkscape:window-width="1855"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:document-units="px" />
|
||||
<g
|
||||
id="layer1"
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
transform="translate(-319.71,-475.23)">
|
||||
<g
|
||||
id="g3620"
|
||||
transform="matrix(2.2116072,0,0,2.2116072,-246.9006,-632.55508)">
|
||||
<path
|
||||
style="fill:#ffffff"
|
||||
sodipodi:nodetypes="sssssssss"
|
||||
inkscape:connector-curvature="0"
|
||||
d="m 298.70134,500.89595 -38.10593,0 c -2.4922,0 -4.39684,1.9053 -4.39684,4.39684 l 0,38.10527 c 0,2.4922 1.90464,4.39749 4.39684,4.39749 l 38.10593,0 c 2.49154,0 4.39683,-1.90529 4.39683,-4.39749 l 0,-38.10527 c 0,-2.49154 -1.90529,-4.39684 -4.39683,-4.39684 z"
|
||||
id="path3618" />
|
||||
<path
|
||||
id="path3497"
|
||||
d="m 298.70134,500.89595 -38.10593,0 c -2.4922,0 -4.39684,1.9053 -4.39684,4.39684 l 0,38.10527 c 0,2.4922 1.90464,4.39749 4.39684,4.39749 l 38.10593,0 c 2.49154,0 4.39683,-1.90529 4.39683,-4.39749 l 0,-38.10527 c 0,-2.49154 -1.90529,-4.39684 -4.39683,-4.39684 z m 1.4643,42.50211 c 0,0.88068 -0.58624,1.46561 -1.4643,1.46561 l -38.10593,0 c -0.87937,0 -1.46561,-0.58624 -1.46561,-1.46561 l 0,-38.10527 c 0,-0.88002 0.58624,-1.46561 1.46561,-1.46561 l 38.10593,0 c 0.88002,0 1.4643,0.58624 1.4643,1.46561 l 0,38.10527 z"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path3499"
|
||||
d="m 291.81296,519.06955 0.14787,-0.73216 c 0.14721,-0.43968 -0.14787,-0.87936 -0.58625,-1.02527 -0.43968,-0.14722 -0.88002,0.14722 -1.02593,0.58625 l -0.14721,0.73215 c -0.87937,-0.14656 -1.61218,0 -1.75874,0.43968 l -3.51812,13.33642 4.54274,1.17184 3.51682,-13.33642 c 0.14721,-0.29378 -0.29247,-0.87937 -1.17118,-1.17249 z"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path3501"
|
||||
d="m 294.01138,520.09548 c -0.14787,0 -0.441,0.14721 -0.441,0.29246 l -1.75873,6.59526 0.7328,0.14722 1.75874,-6.59526 c 0.001,-0.14656 0.001,-0.43968 -0.29181,-0.43968 z"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path3503"
|
||||
d="m 286.24363,536.80411 c 0,0 1.17249,-1.02593 2.05251,-1.90529 l -2.93057,-0.73346 c 0.29116,1.17314 0.87806,2.63875 0.87806,2.63875 z"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="path3505"
|
||||
d="m 285.36361,536.07065 -0.29247,0 c -0.73281,0 -1.46496,0.14722 -4.39749,0.14722 -0.29247,0 -0.58625,-0.14722 -0.88003,-0.43969 -0.43902,-0.29312 -0.7328,-0.58624 -1.31905,-0.58624 l 0,0 c -0.43968,0 -0.88002,0.29247 -1.90529,0.73215 -0.43969,0.29312 -1.17249,0.58625 -1.46562,0.73346 0,-0.14787 0,-0.58624 0.14657,-0.73346 0.14721,-0.58624 0.14721,-1.17183 0,-1.4643 -0.14657,-0.14722 -0.29247,-0.14722 -0.43969,-0.14722 l 0,0 c -0.87937,0 -1.75873,1.02528 -2.78466,1.90464 -0.58494,0.5869 -1.31775,1.31906 -1.61087,1.31906 l 0,0 c -0.29312,-0.43969 0.73281,-2.4922 1.02593,-3.3696 0.58625,-1.46562 0.87937,-1.75874 0.43969,-2.05252 -0.14722,-0.14721 -0.29313,-0.14721 -0.73281,0 -0.58625,0.43969 -1.31905,1.02462 -1.9053,1.75874 -1.31905,1.17183 -2.6381,2.49285 -3.66403,2.05186 -0.14721,-0.14657 -0.43968,0 -0.58624,0.14917 -0.14657,0.14722 0,0.43969 0.14721,0.5856 1.46496,0.73215 2.93057,-0.73347 4.54275,-2.19777 0.43968,-0.29247 0.7328,-0.73215 1.17249,-1.02462 -0.14657,0.14656 -0.14657,0.29247 -0.29313,0.43968 -0.87936,1.9053 -1.46495,3.3696 -1.02527,4.10307 0.14656,0.14721 0.29312,0.29115 0.58624,0.29115 l 0,0 c 0.58625,0 1.31971,-0.73215 2.19842,-1.61151 0.73281,-0.73347 1.46562,-1.46562 2.05186,-1.61283 0,0.14721 0,0.43968 -0.14656,0.73346 -0.14722,0.58624 -0.14722,1.17183 0,1.46561 0.14591,0.14329 0.29247,0.29051 0.43903,0.29051 0.43968,0 1.02593,-0.29247 1.9053,-0.87937 0.43968,-0.29378 1.31905,-0.73346 1.46561,-0.73346 l 0,0 c 0.29247,0 0.58624,0.14721 0.87937,0.43968 0.29312,0.29378 0.7328,0.58625 1.31905,0.58625 3.07713,0 3.66534,0 4.54274,-0.14657 l 0.29312,0 c 0.14657,0 0.29247,-0.14721 0.29247,-0.44099 6.6e-4,-0.29116 0.29378,-0.29116 6.6e-4,-0.29116 z"
|
||||
inkscape:connector-curvature="0" />
|
||||
<rect
|
||||
id="rect3507"
|
||||
height="2.9312253"
|
||||
width="29.312252"
|
||||
y="510.569"
|
||||
x="264.99225" />
|
||||
<rect
|
||||
id="rect3509"
|
||||
height="2.9305711"
|
||||
width="18.467373"
|
||||
y="516.43146"
|
||||
x="264.99225" />
|
||||
<rect
|
||||
id="rect3511"
|
||||
height="2.9312253"
|
||||
width="18.467373"
|
||||
y="523.75952"
|
||||
x="264.99225" />
|
||||
</g>
|
||||
<g
|
||||
id="g3513"
|
||||
transform="matrix(0.65429135,0,0,0.65429135,252.06849,472.60499)" />
|
||||
<g
|
||||
id="g3515"
|
||||
transform="matrix(0.65429135,0,0,0.65429135,252.06849,472.60499)" />
|
||||
<g
|
||||
id="g3517"
|
||||
transform="matrix(0.65429135,0,0,0.65429135,252.06849,472.60499)" />
|
||||
<g
|
||||
id="g3519"
|
||||
transform="matrix(0.65429135,0,0,0.65429135,252.06849,472.60499)" />
|
||||
<g
|
||||
id="g3521"
|
||||
transform="matrix(0.65429135,0,0,0.65429135,252.06849,472.60499)" />
|
||||
<g
|
||||
id="g3523"
|
||||
transform="matrix(0.65429135,0,0,0.65429135,252.06849,472.60499)" />
|
||||
<g
|
||||
id="g3525"
|
||||
transform="matrix(0.65429135,0,0,0.65429135,252.06849,472.60499)" />
|
||||
<g
|
||||
id="g3527"
|
||||
transform="matrix(0.65429135,0,0,0.65429135,252.06849,472.60499)" />
|
||||
<g
|
||||
id="g3529"
|
||||
transform="matrix(0.65429135,0,0,0.65429135,252.06849,472.60499)" />
|
||||
<g
|
||||
id="g3531"
|
||||
transform="matrix(0.65429135,0,0,0.65429135,252.06849,472.60499)" />
|
||||
<g
|
||||
id="g3533"
|
||||
transform="matrix(0.65429135,0,0,0.65429135,252.06849,472.60499)" />
|
||||
<g
|
||||
id="g3535"
|
||||
transform="matrix(0.65429135,0,0,0.65429135,252.06849,472.60499)" />
|
||||
<g
|
||||
id="g3537"
|
||||
transform="matrix(0.65429135,0,0,0.65429135,252.06849,472.60499)" />
|
||||
<g
|
||||
id="g3539"
|
||||
transform="matrix(0.65429135,0,0,0.65429135,252.06849,472.60499)" />
|
||||
<g
|
||||
id="g3541"
|
||||
transform="matrix(0.65429135,0,0,0.65429135,252.06849,472.60499)" />
|
||||
<g
|
||||
transform="scale(1.0411221,0.9605021)"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:condensed;font-size:25.9893322px;line-height:125%;font-family:'Liberation Sans Narrow';-inkscape-font-specification:'Liberation Sans Narrow Bold Condensed';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#808080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
id="text3390">
|
||||
<path
|
||||
d="m 326.62742,615.79017 q 0,-1.56089 0.25381,-2.86797 0.26649,-1.31977 0.79947,-2.25884 0.53299,-0.95175 1.34515,-1.48474 0.82486,-0.53298 1.95428,-0.53298 1.12942,0 2.01773,0.58374 0.8883,0.58375 1.38322,1.89083 0,-0.26649 0.0127,-0.60913 0.0127,-0.35532 0.0381,-0.67257 0.0254,-0.32994 0.0508,-0.58375 0.0381,-0.2538 0.0508,-0.34263 l 2.8172,0 q -0.0254,0.45684 -0.0508,1.2817 -0.0127,0.82486 -0.0127,1.87814 l 0,15.96415 -2.90603,0 0,-5.71055 q 0,-0.35532 0.0127,-0.69796 0.0127,-0.34263 0.0127,-0.6345 0,0 0.0254,-0.63451 l -0.0254,0 q -0.50761,1.33246 -1.45936,1.94159 -0.93907,0.59643 -2.20808,0.59643 -1.06597,0 -1.84007,-0.53298 -0.77409,-0.53298 -1.2817,-1.48474 -0.5076,-0.95176 -0.74871,-2.24615 -0.24112,-1.30708 -0.24112,-2.84258 z m 7.77904,-0.0761 q 0,-1.37054 -0.21573,-2.25884 -0.20305,-0.88831 -0.54568,-1.4086 -0.32994,-0.53299 -0.76141,-0.73603 -0.43146,-0.21573 -0.86292,-0.21573 -0.55837,0 -1.00252,0.26649 -0.43146,0.26649 -0.73603,0.83755 -0.29187,0.57105 -0.45684,1.45936 -0.15228,0.88831 -0.15228,2.13194 0,4.66995 2.32229,4.66995 0.43146,0 0.86292,-0.22842 0.44416,-0.22842 0.78679,-0.77409 0.34263,-0.54568 0.54568,-1.45937 0.21573,-0.91368 0.21573,-2.28421 z"
|
||||
style="fill:#808080"
|
||||
id="path3409" />
|
||||
<path
|
||||
d="m 343.16263,622.87124 q -1.29439,0 -1.99235,-0.85023 -0.68526,-0.86293 -0.68526,-2.60147 l 0,-8.09629 -1.43398,0 0,-2.41112 1.57357,0 0.91369,-3.22329 1.84006,0 0,3.22329 2.13194,0 0,2.41112 -2.13194,0 0,7.13184 q 0,1.00252 0.30456,1.48474 0.31726,0.46953 0.96445,0.46953 0.26649,0 0.48222,-0.0508 0.22843,-0.0508 0.50761,-0.1269 l 0,2.20808 q -0.54568,0.21573 -1.1548,0.31725 -0.59644,0.11421 -1.31977,0.11421 z"
|
||||
style="fill:#808080"
|
||||
id="path3411" />
|
||||
<path
|
||||
d="m 353.18781,623.36616 q -0.40608,1.20556 -0.82486,2.08118 -0.41877,0.87561 -0.92637,1.44667 -0.49492,0.58374 -1.12942,0.86292 -0.62182,0.27919 -1.45937,0.27919 -0.45684,0 -0.93906,-0.0381 -0.46954,-0.0381 -0.901,-0.12691 l 0,-2.53802 q 0.20304,0.0381 0.49491,0.0634 0.30457,0.0381 0.50761,0.0381 0.43146,0 0.7614,-0.11422 0.34264,-0.11421 0.60913,-0.3807 0.26649,-0.26649 0.49491,-0.71064 0.24112,-0.44416 0.45685,-1.11673 l 0.19035,-0.60913 -4.45423,-13.5911 3.09639,0 1.76392,6.43388 q 0,0 0.1269,0.46954 l 0.20304,0.7614 0.22843,0.88831 0.21573,0.88831 q 0.10152,0.43146 0.16497,0.7614 0.0761,0.32995 0.11421,0.46954 0.0254,-0.13959 0.0888,-0.46954 l 0.16497,-0.73602 0.19035,-0.86293 0.20305,-0.87562 0.16497,-0.74871 q 0.0761,-0.34264 0.1269,-0.49492 l 1.6624,-6.48464 3.05832,0 -4.45423,14.45403 z"
|
||||
style="fill:#808080"
|
||||
id="path3413" />
|
||||
<path
|
||||
d="m 364.60891,611.95776 0,-2.8299 10.68506,0 0,2.8299 -10.68506,0 z m 0,6.99224 0,-2.80451 10.68506,0 0,2.80451 -10.68506,0 z"
|
||||
style="fill:#808080"
|
||||
id="path3415" />
|
||||
<path
|
||||
d="m 390.62362,622.64282 -2.62686,-4.97452 -2.63954,4.97452 -3.12176,0 4.12428,-7.09377 -3.92124,-6.63692 3.15983,0 2.39843,4.4923 2.39843,-4.4923 3.17253,0 -3.92124,6.59885 4.14966,7.13184 -3.17252,0 z"
|
||||
style="fill:#808080"
|
||||
id="path3417" />
|
||||
<path
|
||||
d="m 407.3111,615.42215 0,5.17757 -2.34767,0 0,-5.17757 -4.14966,0 0,-2.84258 4.14966,0 0,-5.17756 2.34767,0 0,5.17756 4.18774,0 0,2.84258 -4.18774,0 z"
|
||||
style="fill:#808080"
|
||||
id="path3419" />
|
||||
<path
|
||||
d="m 425.57217,623.36616 q -0.40609,1.20556 -0.82486,2.08118 -0.41877,0.87561 -0.92638,1.44667 -0.49491,0.58374 -1.12942,0.86292 -0.62181,0.27919 -1.45936,0.27919 -0.45684,0 -0.93907,-0.0381 -0.46953,-0.0381 -0.90099,-0.12691 l 0,-2.53802 q 0.20304,0.0381 0.49491,0.0634 0.30456,0.0381 0.5076,0.0381 0.43147,0 0.76141,-0.11422 0.34263,-0.11421 0.60912,-0.3807 0.2665,-0.26649 0.49492,-0.71064 0.24111,-0.44416 0.45684,-1.11673 l 0.19035,-0.60913 -4.45422,-13.5911 3.09638,0 1.76393,6.43388 q 0,0 0.1269,0.46954 l 0.20304,0.7614 0.22842,0.88831 0.21573,0.88831 q 0.10152,0.43146 0.16497,0.7614 0.0761,0.32995 0.11422,0.46954 0.0254,-0.13959 0.0888,-0.46954 l 0.16497,-0.73602 0.19035,-0.86293 0.20304,-0.87562 0.16497,-0.74871 q 0.0761,-0.34264 0.1269,-0.49492 l 1.66241,-6.48464 3.05831,0 -4.45422,14.45403 z"
|
||||
style="fill:#808080"
|
||||
id="path3421" />
|
||||
</g>
|
||||
</g>
|
||||
<metadata
|
||||
id="metadata499">
|
||||
<rdf:RDF>
|
||||
<cc:Work>
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<cc:license
|
||||
rdf:resource="http://creativecommons.org/publicdomain/zero/1.0/" />
|
||||
<dc:publisher>
|
||||
<cc:Agent
|
||||
rdf:about="http://openclipart.org/">
|
||||
<dc:title>Openclipart</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:publisher>
|
||||
<dc:title>Pile of Golden Coins</dc:title>
|
||||
<dc:date>2010-04-09T03:27:45</dc:date>
|
||||
<dc:description>A pile of hypothetical golden coins, drawn in Inkscape.</dc:description>
|
||||
<dc:source>https://openclipart.org/detail/43969/pile-of-golden-coins-by-j_alves</dc:source>
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>J_Alves</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
<dc:subject>
|
||||
<rdf:Bag>
|
||||
<rdf:li>coin</rdf:li>
|
||||
<rdf:li>currency</rdf:li>
|
||||
<rdf:li>gold</rdf:li>
|
||||
<rdf:li>money</rdf:li>
|
||||
<rdf:li>thaler</rdf:li>
|
||||
</rdf:Bag>
|
||||
</dc:subject>
|
||||
</cc:Work>
|
||||
<cc:License
|
||||
rdf:about="http://creativecommons.org/publicdomain/zero/1.0/">
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Reproduction" />
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Distribution" />
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
|
||||
</cc:License>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 15 KiB |
4
contract_variable_quantity/tests/__init__.py
Normal file
4
contract_variable_quantity/tests/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from . import test_contract_variable_quantity
|
||||
@@ -0,0 +1,60 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# © 2016 Pedro M. Baeza <pedro.baeza@tecnativa.com>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from openerp.tests import common
|
||||
from openerp import exceptions
|
||||
|
||||
|
||||
class TestContractVariableQuantity(common.SavepointCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestContractVariableQuantity, cls).setUpClass()
|
||||
cls.partner = cls.env['res.partner'].create({
|
||||
'name': 'Test partner',
|
||||
})
|
||||
cls.product = cls.env['product.product'].create({
|
||||
'name': 'Test product',
|
||||
})
|
||||
cls.contract = cls.env['account.analytic.account'].create({
|
||||
'name': 'Test Contract',
|
||||
'partner_id': cls.partner.id,
|
||||
'pricelist_id': cls.partner.property_product_pricelist.id,
|
||||
'recurring_invoices': True,
|
||||
})
|
||||
cls.formula = cls.env['contract.line.qty.formula'].create({
|
||||
'name': 'Test formula',
|
||||
# For testing each of the possible variables
|
||||
'code': 'env["res.users"]\n'
|
||||
'context.get("lang")\n'
|
||||
'user.id\n'
|
||||
'line.qty_type\n'
|
||||
'contract.id\n'
|
||||
'invoice.id\n'
|
||||
'result = 12',
|
||||
})
|
||||
cls.contract_line = cls.env['account.analytic.invoice.line'].create({
|
||||
'analytic_account_id': cls.contract.id,
|
||||
'product_id': cls.product.id,
|
||||
'name': 'Test',
|
||||
'qty_type': 'variable',
|
||||
'qty_formula_id': cls.formula.id,
|
||||
'quantity': 1,
|
||||
'uom_id': cls.product.uom_id.id,
|
||||
'price_unit': 100,
|
||||
'discount': 50,
|
||||
})
|
||||
|
||||
def test_check_invalid_code(self):
|
||||
with self.assertRaises(exceptions.ValidationError):
|
||||
self.formula.code = "sdsds"
|
||||
|
||||
def test_check_no_return_value(self):
|
||||
with self.assertRaises(exceptions.ValidationError):
|
||||
self.formula.code = "user.id"
|
||||
|
||||
def test_check_variable_quantity(self):
|
||||
self.contract._create_invoice(self.contract)
|
||||
invoice = self.env['account.invoice'].search(
|
||||
[('contract_id', '=', self.contract.id)])
|
||||
self.assertEqual(invoice.invoice_line_ids[0].quantity, 12)
|
||||
90
contract_variable_quantity/views/contract_view.xml
Normal file
90
contract_variable_quantity/views/contract_view.xml
Normal file
@@ -0,0 +1,90 @@
|
||||
<?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']//field[@name='quantity']" position="before">
|
||||
<field name="qty_type"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='recurring_invoice_line_ids']//field[@name='quantity']" position="after">
|
||||
<field name="qty_formula_id"
|
||||
attrs="{'required': [('qty_type', '=', 'variable')], 'invisible': [('qty_type', '!=', 'variable')]}"
|
||||
/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='recurring_invoice_line_ids']//field[@name='quantity']" position="attributes">
|
||||
<attribute name="attrs">{'required': [('qty_type', '=', 'fixed')], 'invisible': [('qty_type', '!=', 'fixed')]}</attribute>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_contract_line_qty_formula_tree" model="ir.ui.view">
|
||||
<field name="model">contract.line.qty.formula</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree>
|
||||
<field name="name"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_contract_line_qty_formula_form" model="ir.ui.view">
|
||||
<field name="model">contract.line.qty.formula</field>
|
||||
<field name="arch" type="xml">
|
||||
<form >
|
||||
<sheet>
|
||||
<div class="oe_title">
|
||||
<h1>
|
||||
<field name="name" placeholder="Name"/>
|
||||
</h1>
|
||||
</div>
|
||||
<group string="Code">
|
||||
<div style="margin-top: 4px;">
|
||||
<field name="code" nolabel="1"/>
|
||||
<h3>Help with Python expressions.</h3>
|
||||
<p>You have to insert valid Python code block that stores at some moment a float/integer value of the quantity to invoice in the variable 'result'.</p>
|
||||
<p>You can use these variables to compute your formula:</p>
|
||||
<ul>
|
||||
<li><i>env</i>: Environment variable for getting other models.</li>
|
||||
<li><i>context</i>: Current context dictionary.</li>
|
||||
<li><i>user</i>: Current user.</li>
|
||||
<li><i>line</i>: Contract recurring invoice line that triggers this formula.</li>
|
||||
<li><i>contract</i>: Contract whose line belongs to.</li>
|
||||
<li><i>invoice</i>: Invoice (header) being created.</li>
|
||||
</ul>
|
||||
<div>
|
||||
<p>Example of Python code</p>
|
||||
<code>
|
||||
result = env['product.product'].search_count([('sale_ok', '=', True)])
|
||||
</code>
|
||||
</div>
|
||||
</div>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_contract_quantity_formula" model="ir.actions.act_window">
|
||||
<field name="name">Formulas (quantity)</field>
|
||||
<field name="res_model">contract.line.qty.formula</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="help" type="html">
|
||||
<p class="oe_view_nocontent_create">
|
||||
Click to create a new formula for variable quantities.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<menuitem id="contract.menu_config_contract"
|
||||
name="Contracts"
|
||||
sequence="5"
|
||||
parent="base.menu_sale_config"
|
||||
/>
|
||||
<menuitem id="menu_contract_quantity_formula"
|
||||
action="action_contract_quantity_formula"
|
||||
parent="contract.menu_config_contract"
|
||||
/>
|
||||
|
||||
</odoo>
|
||||
Reference in New Issue
Block a user