Browse Source
[ADD] contract_variable_quantity:
[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).pull/238/head
Pedro M. Baeza
8 years ago
committed by
sbejaoui
11 changed files with 625 additions and 0 deletions
-
72contract_variable_quantity/README.rst
-
4contract_variable_quantity/__init__.py
-
21contract_variable_quantity/__openerp__.py
-
4contract_variable_quantity/models/__init__.py
-
66contract_variable_quantity/models/contract.py
-
3contract_variable_quantity/security/ir.model.access.csv
-
BINcontract_variable_quantity/static/description/icon.png
-
301contract_variable_quantity/static/description/icon.svg
-
4contract_variable_quantity/tests/__init__.py
-
60contract_variable_quantity/tests/test_contract_variable_quantity.py
-
90contract_variable_quantity/views/contract_view.xml
@ -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. |
@ -0,0 +1,4 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). |
|||
|
|||
from . import models |
@ -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, |
|||
} |
@ -0,0 +1,4 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). |
|||
|
|||
from . import contract |
@ -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.')) |
@ -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 |
After Width: 128 | Height: 128 | Size: 5.1 KiB |
@ -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> |
@ -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) |
@ -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> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue