Skip to content
Extraits de code Groupes Projets
Valider 613f906c rédigé par Benjamin - Le Filament's avatar Benjamin - Le Filament
Parcourir les fichiers

[cgscop #42] ajout calcul auto des distances sur NdF

parent e48e9c53
Branches
Aucune étiquette associée trouvée
Aucune requête de fusion associée trouvée
...@@ -15,13 +15,29 @@ Ce module permet hérite le module *hr_expense* d'Odoo pour ajouter les fonction ...@@ -15,13 +15,29 @@ Ce module permet hérite le module *hr_expense* d'Odoo pour ajouter les fonction
* modification de la form vue *hr_expense* * modification de la form vue *hr_expense*
* ajout d'un journal de note de frais * ajout d'un journal de note de frais
* modification du modèle *hr_expense_sheet* pour mettre le journal de NdF par défaut * modification du modèle *hr_expense_sheet* pour mettre le journal de NdF par défaut
* ajout du calcul automatique des kilomètres
Calcul des KM
-------------
Cette option se configure dans les articles de notes de frais. Pour activer le calcul automatique, il faut configurer un article avec un type de calcul **Prix fixe** et cocher **Calcul automatique de la distance**.
Sur la note de frais, lorsque l'article est sélectionné, les adresses de départ et d'arrivée apparaîssent ainsi que le bouton de calcul. Par défaut, l'adresse de départ est l'adresse de l'UR, l'adresse d'arrivée est l'adresse deu contact de la ligne de temps.
Les 2 adresses doivent être renseignées pour lancer le calcul.
Un arrondi est effctué aux 5km supérieurs.
Le calcul s'appuie sur 2 API :
* **api-adresse.data.gouv.fr** : pour la transformation de l'adresse en coordonnées
* **router.project-osrm.org** : pour le calcul de la distance
Credits Credits
======= =======
Funders Funders
------------ -------
The development of this module has been financially supported by: The development of this module has been financially supported by:
......
# © 2019 Le Filament (<http://www.le-filament.com>) # © 2019 Le Filament (<http://www.le-filament.com>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo import models, fields, api import re
import requests
import logging
from odoo import models, fields, api, exceptions
_logger = logging.getLogger(__name__)
class CGScopExpense(models.Model): class CGScopExpense(models.Model):
...@@ -28,14 +34,29 @@ class CGScopExpense(models.Model): ...@@ -28,14 +34,29 @@ class CGScopExpense(models.Model):
store=True) store=True)
expense_formula = fields.Selection( expense_formula = fields.Selection(
related='product_id.expense_formula') related='product_id.expense_formula')
is_ik = fields.Boolean(
related='product_id.is_ik')
quantity_computed = fields.Float( quantity_computed = fields.Float(
string="Quantité", string="Quantité",
compute="_compute_values") compute="_compute_values")
unit_amount_computed = fields.Float( unit_amount_computed = fields.Float(
string="Prix", string="Prix",
compute="_compute_values") compute="_compute_values")
from_address = fields.Text(
string="Adresse de départ",
compute='_compute_from_address',
store=True)
to_address = fields.Text(
string="Adresse d'arrivée",
compute='_compute_to_address',
store=True)
# ------------------------------------------------------ # ------------------------------------------------------
# Default Fields
# ------------------------------------------------------
def _default_from_address(self):
return self.env.uid
# ------------------------------------------------------
# Computed Fields # Computed Fields
# ------------------------------------------------------ # ------------------------------------------------------
@api.depends('quantity', 'unit_amount') @api.depends('quantity', 'unit_amount')
...@@ -43,6 +64,18 @@ class CGScopExpense(models.Model): ...@@ -43,6 +64,18 @@ class CGScopExpense(models.Model):
self.quantity_computed = self.quantity self.quantity_computed = self.quantity
self.unit_amount_computed = self.unit_amount self.unit_amount_computed = self.unit_amount
@api.depends('employee_id')
def _compute_from_address(self):
if self.employee_id:
self.from_address = self._format_address(
self.employee_id.address_id)
@api.depends('timesheet_id')
def _compute_to_address(self):
if self.timesheet_id:
self.to_address = self._format_address(
self.timesheet_id.partner_id)
# ------------------------------------------------------ # ------------------------------------------------------
# Onchange Fields # Onchange Fields
# ------------------------------------------------------ # ------------------------------------------------------
...@@ -67,3 +100,61 @@ class CGScopExpense(models.Model): ...@@ -67,3 +100,61 @@ class CGScopExpense(models.Model):
elif self.product_id.expense_formula == 'fixed_rate': elif self.product_id.expense_formula == 'fixed_rate':
self.quantity = 1.0 self.quantity = 1.0
self.product_id.unit_amount = self.product_id.standard_price self.product_id.unit_amount = self.product_id.standard_price
# ------------------------------------------------------
# Button function
# ------------------------------------------------------
def get_ik(self):
if not self.from_address or not self.to_address:
raise exceptions.Warning(
"Les adresses de départ et d'arrivée doivent être renseignées")
else:
# retourne la distance entre les 2 adresses
distance = self._get_distance(
self._get_coord(self.from_address),
self._get_coord(self.to_address))
self.quantity = distance
# ------------------------------------------------------
# Global function
# ------------------------------------------------------
def _format_address(self, partner):
address = partner.street
if partner.street2:
address += '\n' + partner.street2
if partner.street3:
address += '\n' + partner.street3
address += '\n' + partner.zip + ' ' + partner.city
return address
def _get_coord(self, address):
addr = re.sub(r"\W", "+", address, flags=re.I)
url = 'https://api-adresse.data.gouv.fr/search/?q=' + addr
try:
response = requests.get(url)
coord = response.json().get('features')[0].get('geometry').get('coordinates')
return str(coord[0]) + ',' + str(coord[1])
except Exception as err:
_logger.warning(
"Erreur de connexion. URL: %s",
err.__str__(),
)
raise exceptions.Warning(
"Erreur de connexion avec l'API")
return False
def _get_distance(self, coord1, coord2):
url = ('http://router.project-osrm.org/route/v1/car/' + coord1 +
';' + coord2 + '?alternatives=false&overview=false')
try:
response = requests.get(url)
dist = response.json().get('routes')[0].get('legs')[0].get('distance')
return dist / 1000
except Exception as err:
_logger.warning(
"Erreur de connexion. URL: %s",
err.__str__(),
)
raise exceptions.Warning(
"Erreur de connexion avec l'API")
return False
...@@ -12,6 +12,7 @@ class CGScopProductTemplate(models.Model): ...@@ -12,6 +12,7 @@ class CGScopProductTemplate(models.Model):
return self.env['res.company']._ur_default_get() return self.env['res.company']._ur_default_get()
expense_gap = fields.Float("Plafond de dépense") expense_gap = fields.Float("Plafond de dépense")
is_ik = fields.Boolean("Calcul automatique de la distance")
expense_formula = fields.Selection( expense_formula = fields.Selection(
[('free', 'Libre'), [('free', 'Libre'),
('fixed_rate', 'Forfait'), ('fixed_rate', 'Forfait'),
...@@ -33,6 +34,11 @@ class CGScopProductTemplate(models.Model): ...@@ -33,6 +34,11 @@ class CGScopProductTemplate(models.Model):
on_delete='restrict', on_delete='restrict',
default=_default_ur) default=_default_ur)
@api.onchange('expense_formula')
def _onchange_expense_formula(self):
if self.expense_formula != 'fixed_price':
self.is_ik = False
class CGScopProductProduct(models.Model): class CGScopProductProduct(models.Model):
_inherit = 'product.product' _inherit = 'product.product'
......
...@@ -61,11 +61,26 @@ ...@@ -61,11 +61,26 @@
<attribute name="attrs">{'invisible': [('expense_formula', '=', 'fixed_rate')]}</attribute> <attribute name="attrs">{'invisible': [('expense_formula', '=', 'fixed_rate')]}</attribute>
</label> </label>
<field name="quantity" position="attributes"> <field name="quantity" position="attributes">
<attribute name="attrs">{'invisible': [('expense_formula', '=', 'fixed_rate')]}</attribute> <attribute name="attrs">{'invisible': [('expense_formula', '=', 'fixed_rate')], 'readonly': [('is_ik', '=', True)]}</attribute>
</field> </field>
<!-- gestion des adresses de départ et d'arrivée -->
<xpath expr="//group/group[2]" position="after">
<group attrs="{'invisible':[('is_ik','!=', True)], 'required':[('is_ik','=', True)]}">
<field name="from_address" readonly="False"/>
</group>
<group attrs="{'invisible':[('is_ik','!=', True)], 'required':[('is_ik','=', True)]}">
<field name="to_address" readonly="False"/>
</group>
<group attrs="{'invisible':[('is_ik','!=', True)], 'required':[('is_ik','=', True)]}">
<button type="object" name="get_ik" string="Calcul distance" class="btn-primary"></button>
</group>
<group attrs="{'invisible':[('is_ik','!=', True)], 'required':[('is_ik','=', True)]}"></group>
</xpath>
<field name="tax_ids" position="before"> <field name="tax_ids" position="before">
<field name="expense_gap" attrs="{'invisible': [('expense_gap', '=', 0.0)]}" readonly="True" /> <field name="expense_gap" attrs="{'invisible': [('expense_gap', '=', 0.0)]}" readonly="True" />
<field name="expense_formula" invisible="True" /> <field name="expense_formula" invisible="True" />
<field name="is_ik" invisible="True" />
</field> </field>
<field name="analytic_account_id" position="before"> <field name="analytic_account_id" position="before">
<field name="timesheet_id" options="{'no_create': True, 'no_open': True}" domain="[('employee_id', '=', employee_id )]"/> <field name="timesheet_id" options="{'no_create': True, 'no_open': True}" domain="[('employee_id', '=', employee_id )]"/>
......
...@@ -50,6 +50,7 @@ ...@@ -50,6 +50,7 @@
</field> </field>
<field name="uom_po_id" position="after"> <field name="uom_po_id" position="after">
<field name="expense_gap" /> <field name="expense_gap" />
<field name="is_ik" widget="boolean_toggle" attrs="{'invisible':[('expense_formula','!=','fixed_price')],]}"/>
<field name="ur_id" options="{'no_open': True, 'no_create': True}"/> <field name="ur_id" options="{'no_open': True, 'no_create': True}"/>
</field> </field>
</field> </field>
......
0% Chargement en cours ou .
You are about to add 0 people to the discussion. Proceed with caution.
Veuillez vous inscrire ou vous pour commenter