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