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

[update] add PCA N-1 and FAE N-1 calculation, delete report

parent 08e6d1ed
Aucune branche associée trouvée
Aucune étiquette associée trouvée
Aucune requête de fusion associée trouvée
......@@ -10,9 +10,7 @@ Le Filament PCA/FAE
Description
===========
Ce module ajoute une table dans les commandes et les factures permettant de ventiler les montants par année.
Il ajoute également une vue pour agréger les calculs et afficher une table par année du PCA, FAE, montant facturé et montant prévisionnel.
Ce module ajoute une table dans les commandes et les factures permettant de ventiler les montants par année fiscale.
Ces informations sont accessibles dans le menu *Comptabilité > Rapports > CA / FAE*
......@@ -33,7 +31,6 @@ Configuration
Il est essentiel de configurer le jour et le mois de cloture fiscale pour que le calcul des années fiscales se fasse correctement sur les factures.
Si des factures sont déjà présentes, il faut également recomputer le champ **fiscal_year** sur les factures client.
Credits
=======
......
......@@ -3,4 +3,3 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from . import models
from . import reports
......@@ -17,7 +17,6 @@
'views/account_invoice_pcafae_line.xml',
'views/sale_order.xml',
'views/sale_order_pcafae_line.xml',
'reports/pca_fae_dashboard.xml',
'views/menus.xml'
],
}
......@@ -22,6 +22,12 @@ class AccountInvoicePcaFae(models.Model):
compute='_get_fiscal_year',
store=True
)
is_weighted = fields.Boolean(
string='Ventilé',
compute='_compute_is_weighted',
store=True,
default=False
)
# ------------------------------------------------------
# Constrains function
......@@ -31,6 +37,8 @@ class AccountInvoicePcaFae(models.Model):
total = sum(self.invoice_pcafae_line_ids.mapped('year_percent'))
if total != 100.0 and self.invoice_pcafae_line_ids:
raise UserError("Le total des pourcentages doit être égal à 100 !")
if self.invoice_pcafae_line_ids and not str(self.fiscal_year) in self.invoice_pcafae_line_ids.mapped('year'):
raise UserError("L'année fiscale de la facture doit être présente dans la ventilation (à 0% si nécessaire).")
return True
# ------------------------------------------------------
......@@ -55,3 +63,38 @@ class AccountInvoicePcaFae(models.Model):
invoice.fiscal_year = str(date_real.year + 1)
else:
invoice.fiscal_year = str(date_real.year)
@api.multi
@api.depends('invoice_pcafae_line_ids')
def _compute_is_weighted(self):
for inv in self:
if inv.invoice_pcafae_line_ids:
inv.is_weighted = True
else:
inv.is_weighted = False
# ------------------------------------------------------
# Override ORM
# ------------------------------------------------------
@api.multi
def write(self, values):
"""
Calcule le PCA et FAE N-1 pour l'année en cours par rapport à
l'ensemble des lignes pondérées
:param values:
:return: self
"""
res = super(AccountInvoicePcaFae, self).write(values)
if values.get('invoice_pcafae_line_ids', False):
if len(self.invoice_pcafae_line_ids) > 1:
line_fy_id = self.invoice_pcafae_line_ids.filtered(
lambda y: y.year == str(self.fiscal_year))
line_ids = self.invoice_pcafae_line_ids - line_fy_id
for line in line_ids:
# si FAE, on affecte le FAE négatif sur l'année en cours
if line.fae:
line_fy_id.fae_last_year = -line.fae
# si PCA, on affecte le PCA négatif sur l'année en cours
elif line.pca_last_year:
line_fy_id.pca = -line.pca_last_year
return res
\ No newline at end of file
......@@ -15,19 +15,18 @@ class AccountInvoicePcaFaeLines(models.Model):
_order = 'year, partner_id'
year = fields.Char(
string='Année Fiscal Interne',
string='Année Fiscale',
default=str(datetime.now().year),
required=True
)
invoice_id = fields.Many2one(
comodel_name='account.invoice',
string='Facture',
required=True
)
partner_id = fields.Many2one(
comodel_name='res.partner',
string='Client',
related='invoice_id.partner_id',
string='Client',
store=True
)
date = fields.Date(
......@@ -35,7 +34,7 @@ class AccountInvoicePcaFaeLines(models.Model):
related='invoice_id.date_invoice',
store=True
)
state = fields.Selection(
invoice_state = fields.Selection(
string='Status',
related='invoice_id.state',
store=True
......@@ -46,19 +45,50 @@ class AccountInvoicePcaFaeLines(models.Model):
change_default=True, required=True, readonly=True,
default=lambda self: self.env['res.company']._company_default_get(
'account.invoice.pcafae.line'))
company_currency_id = fields.Many2one(
currency_id = fields.Many2one(
comodel_name='res.currency',
related='company_id.currency_id',
string="Company Currency",
readonly=True
)
year_percent = fields.Float('Pourcentage', default=0.0)
year_amount = fields.Monetary(
string='Montant HT',
compute='_compute_year_amount',
# Invoice computed fields
fae = fields.Float(
'FAE',
currency_field='company_currency_id',
store=True
)
compute='_compute_invoice',
store=True,
default=0.0)
fae_last_year = fields.Float(
'FAE N-1',
currency_field='company_currency_id',
compute='_compute_invoice',
store=True,
default=0.0)
pca = fields.Float(
'PCA',
currency_field='company_currency_id',
compute='_compute_invoice',
store=True,
default=0.0)
pca_last_year = fields.Float(
'PCA N-1',
currency_field='company_currency_id',
compute='_compute_invoice',
store=True,
default=0.0)
ca = fields.Float(
'CA',
currency_field='company_currency_id',
compute='_compute_invoice',
store=True,
default=0.0)
real = fields.Float(
'Facturé réel',
currency_field='company_currency_id',
compute='_compute_invoice',
store=True,
default=0.0)
# ------------------------------------------------------
# Contraintes SQL
......@@ -73,12 +103,21 @@ class AccountInvoicePcaFaeLines(models.Model):
# ------------------------------------------------------
# Compute function
# ------------------------------------------------------
@api.depends('year_percent', 'invoice_id.amount_untaxed_signed')
@api.depends(
'year_percent',
'invoice_id.amount_untaxed_signed',
'invoice_id.fiscal_year',)
@api.multi
def _compute_year_amount(self):
def _compute_invoice(self):
for line in self:
line.year_amount = (0.01 * line.year_percent *
line.ca = (0.01 * line.year_percent *
line.invoice_id.amount_untaxed_signed)
if line.year < line.invoice_id.fiscal_year:
line.fae = line.ca
elif line.year == line.invoice_id.fiscal_year:
line.real = line.invoice_id.amount_untaxed_signed
elif line.year > line.invoice_id.fiscal_year:
line.pca_last_year = line.ca
# ------------------------------------------------------
# Onchange function
......
......@@ -17,6 +17,12 @@ class SaleOrderPcaFae(models.Model):
inverse_name='order_id',
string='Pondération annuelle'
)
is_weighted = fields.Boolean(
string='Ventilé',
compute='_compute_is_weighted',
store=True,
default=False
)
# ------------------------------------------------------
# Constrains function
......@@ -29,6 +35,17 @@ class SaleOrderPcaFae(models.Model):
return True
# ------------------------------------------------------
# Computed fields
# ------------------------------------------------------
@api.multi
@api.depends('order_pcafae_line_ids')
def _compute_is_weighted(self):
for so in self:
if so.order_pcafae_line_ids:
so.is_weighted = True
else:
so.is_weighted = False
# ------------------------------------------------------
# Override parent
# ------------------------------------------------------
@api.multi
......
......@@ -16,13 +16,13 @@ class SaleOrderPcaFaeLines(models.Model):
_order = 'year, partner_id'
year = fields.Char(
string='Année Fiscale Interne',
string='Année Fiscale',
default=str(datetime.now().year),
required=True
)
order_id = fields.Many2one(
comodel_name='sale.order',
string='Facture',
string='Commande',
required=True
)
partner_id = fields.Many2one(
......
# -*- coding: utf-8 -*-
# © 2020 Le Filament (<http://www.le-filament.com>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from . import pca_fae_dashboard
# -*- coding: utf-8 -*-
# © 2020 Le Filament (<http://www.le-filament.com>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from datetime import datetime
from odoo import fields, models, api, tools
class PcaFaeDashbooard(models.Model):
_name = 'pca.fae.dashboard'
_description = 'Vue PCA / FAE'
_order = 'year'
_auto = False
# Vue
year = fields.Char('Année Fiscale Interne')
partner_id = fields.Many2one(
comodel_name='res.partner',
string='Client'
)
order_estimated = fields.Float(string='Commandes pondérées')
invoiced = fields.Float(string='Facturé réel')
ca = fields.Float(string='CA')
pca = fields.Float(string='PCA')
fae = fields.Float(string='FAE')
# ------------------------------------------------------
# Model definition
# ------------------------------------------------------
@api.model
def _select(self):
query = """
SELECT
ROW_NUMBER() OVER(ORDER BY an.year, an.partner_id) as id,
an.partner_id,
an.year,
previ.order_estimated,
inv_pca.ca,
inv.invoiced,
(inv.invoiced - inv_pca.ca) as pca,
(previ.order_estimated - inv_pca.ca) as fae
FROM ( (
SELECT DISTINCT ON (partner_id, date_part('year', confirmation_date))
partner_id, date_part('year', confirmation_date)::VARCHAR as year
FROM sale_order WHERE state IN ('sale', 'done')
)
UNION (
SELECT DISTINCT ON (partner_id, date_part('year', date_invoice))
partner_id, date_part('year', date_invoice)::VARCHAR as year
FROM account_invoice WHERE state IN ('oprn', 'paid')
)
UNION (
SELECT DISTINCT ON (partner_id, year)
partner_id, year
FROM sale_order_pcafae_line
)
UNION (
SELECT DISTINCT ON (partner_id, year)
partner_id, year
FROM account_invoice_pcafae_line
)) AS an
LEFT JOIN (
SELECT
year,
partner_id,
sum(year_amount) as order_estimated
FROM sale_order_pcafae_line
WHERE state in ('sale', 'done')
GROUP BY year, partner_id
) previ
ON (an.partner_id = previ.partner_id and an.year = previ.year)
LEFT JOIN (
SELECT
year,
partner_id,
sum(year_amount) as ca
FROM account_invoice_pcafae_line
WHERE state in ('open', 'paid')
GROUP BY year, partner_id
) inv_pca
ON (an.partner_id = inv_pca.partner_id and an.year = inv_pca.year)
LEFT JOIN (
SELECT
fiscal_year,
partner_id,
sum(amount_untaxed_signed) as invoiced
FROM account_invoice
WHERE state in ('open', 'paid') and type in ('out_invoice', 'out_refund')
GROUP BY fiscal_year, partner_id
) inv
ON (an.partner_id = inv.partner_id and an.year = inv.fiscal_year)
ORDER BY an.year, an.partner_id
"""
return query
def init(self):
tools.drop_view_if_exists(self.env.cr, self._table)
self.env.cr.execute("""CREATE or REPLACE VIEW %s as (
%s
)""" % (self._table, self._select()))
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="pca_fae_dashboard_tree_view" model="ir.ui.view">
<field name="name">pca.fae.dashboard.tree</field>
<field name="model">pca.fae.dashboard</field>
<field name="arch" type="xml">
<tree string="Dashboard PCA FAE" create="false" edit="false" delete="false" >
<field name="year"/>
<field name="partner_id"/>
<field name="order_estimated"/>
<field name="invoiced"/>
<field name="ca"/>
<field name="pca"/>
<field name="fae"/>
</tree>
</field>
</record>
<record id="pca_fae_dashboard_search_view" model="ir.ui.view">
<field name="name">pca.fae.dashboard.search</field>
<field name="model">pca.fae.dashboard</field>
<field name="arch" type="xml">
<search string="Dashboard PCA FAE">
<field name="partner_id"/>
<field name="year"/>
<filter string="Cette année" name="this_year"
domain="[('year','=',(context_today()).strftime('%%Y'))]"/>
<filter string="L'année précédente" name="prev_year"
domain="[('year','=',(context_today() - relativedelta(years=1)).strftime('%%Y'))]"/>
</search>
</field>
</record>
<record id="pca_fae_dashboard_pivot_view" model="ir.ui.view">
<field name="name">pca.fae.dashboard.pivot</field>
<field name="model">pca.fae.dashboard</field>
<field name="arch" type="xml">
<pivot string="Dashboard PCA FAE">
<field name="year" type="row"/>
<field name="order_estimated" type="measure"/>
<field name="invoiced" type="measure"/>
<field name="ca" type="measure"/>
<field name="pca" type="measure"/>
<field name="fae" type="measure"/>
</pivot>
</field>
</record>
<record id="pca_fae_dashboard_graph_view" model="ir.ui.view">
<field name="name">pca.fae.dashboard.graph</field>
<field name="model">pca.fae.dashboard</field>
<field name="arch" type="xml">
<graph string="Dashboard PCA FAE">
<field name="year"/>
<field name="ca" type="measure"/>
</graph>
</field>
</record>
<record id="pca_fae_dashboard_act_window" model="ir.actions.act_window">
<field name="name">Dashboard PCA FAE</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">pca.fae.dashboard</field>
<field name="view_type">form</field>
<field name="view_mode">pivot,tree,graph</field>
</record>
</data>
</odoo>
\ No newline at end of file
......@@ -18,13 +18,39 @@
<tree editable="bottom">
<field name="year"/>
<field name="year_percent"/>
<field name="year_amount" sum="Total"/>
<field name="company_currency_id" invisible="1"/>
<field name="ca" sum="Total" widget="monetary"/>
<field name="currency_id" invisible="1"/>
</tree>
</field>
</page>
</xpath>
</field>
</record>
<record id="invoice_tree" model="ir.ui.view">
<field name="name">account.invoice.tree</field>
<field name="model">account.invoice</field>
<field name="inherit_id" ref="account.invoice_tree"/>
<field name="arch" type="xml">
<field name="type" position="after">
<field name="is_weighted"/>
</field>
</field>
</record>
<record id="view_account_invoice_filter" model="ir.ui.view">
<field name="name">account.invoice.search</field>
<field name="model">account.invoice</field>
<field name="inherit_id" ref="account.view_account_invoice_filter"/>
<field name="arch" type="xml">
<filter name="late" position="after">
<separator/>
<filter string="Non ventilé" name="is_weighted" domain="[('is_weighted','=',False)]"/>
</filter>
</field>
</record>
</data>
</odoo>
\ No newline at end of file
......@@ -8,10 +8,16 @@
<tree string="Pondération Factures" create="false" edit="false" delete="false" >
<field name="year"/>
<field name="partner_id"/>
<field name="invoice_id"/>
<field name="year_percent"/>
<field name="year_amount"/>
<field name="invoice_id" widget="many2one_clickable"/>
<field name="year_percent"> %</field>
<field name="real" widget="monetary"/>
<field name="pca" widget="monetary"/>
<field name="pca_last_year" widget="monetary"/>
<field name="ca" widget="monetary"/>
<field name="fae" widget="monetary"/>
<field name="fae_last_year" widget="monetary"/>
<field name="date"/>
<field name="currency_id" invisible="1"/>
</tree>
</field>
</record>
......@@ -34,7 +40,10 @@
<field name="arch" type="xml">
<pivot string="Pondération Factures">
<field name="year" type="row"/>
<field name="year_amount" type="measure"/>
<field name="real" type="measure"/>
<field name="pca" type="measure"/>
<field name="ca" type="measure"/>
<field name="fae" type="measure"/>
</pivot>
</field>
</record>
......@@ -45,7 +54,7 @@
<field name="arch" type="xml">
<graph string="Pondération Factures">
<field name="year"/>
<field name="year_amount" type="measure"/>
<field name="ca" type="measure"/>
</graph>
</field>
</record>
......@@ -55,8 +64,8 @@
<field name="type">ir.actions.act_window</field>
<field name="res_model">account.invoice.pcafae.line</field>
<field name="view_type">form</field>
<field name="view_mode">tree,pivot,graph</field>
<field name="domain" eval="[('state', 'in', ('open', 'paid'))]"/>
<field name="view_mode">pivot,tree,graph</field>
<field name="domain" eval="[('invoice_state', 'in', ('open', 'paid'))]"/>
</record>
</data>
</odoo>
\ No newline at end of file
......@@ -3,12 +3,10 @@
<data>
<menuitem name="PCA / FAE" id="pca_fae_root_menu" parent="account.menu_finance_reports" sequence="10"/>
<menuitem name="Rapport PCA / FAE" id="pca_fae_dashboard_menu" parent="lefilament_pca_fae.pca_fae_root_menu"
action="lefilament_pca_fae.pca_fae_dashboard_act_window" sequence="10"/>
<menuitem name="Pondération Factures" id="account_invoice_pcafae_line_menu" parent="lefilament_pca_fae.pca_fae_root_menu"
<menuitem name="Prévisionnel" id="sale_order_pcafae_line_menu" parent="lefilament_pca_fae.pca_fae_root_menu"
action="lefilament_pca_fae.sale_order_pcafae_line_act_window" sequence="10"/>
<menuitem name="Ventilation Facturé" id="account_invoice_pcafae_line_menu" parent="lefilament_pca_fae.pca_fae_root_menu"
action="lefilament_pca_fae.account_invoice_pcafae_line_act_window" sequence="20"/>
<menuitem name="Pondération Commandes" id="sale_order_pcafae_line_menu" parent="lefilament_pca_fae.pca_fae_root_menu"
action="lefilament_pca_fae.sale_order_pcafae_line_act_window" sequence="30"/>
</data>
</odoo>
\ No newline at end of file
......@@ -14,7 +14,7 @@
<tree editable="bottom">
<field name="year"/>
<field name="year_percent"/>
<field name="year_amount" sum="Total"/>
<field name="year_amount" sum="Total" widget="monetary"/>
<field name="company_currency_id" invisible="1"/>
</tree>
</field>
......@@ -22,5 +22,29 @@
</xpath>
</field>
</record>
<record id="view_order_tree" model="ir.ui.view">
<field name="name">sale.order.tree</field>
<field name="model">sale.order</field>
<field name="inherit_id" ref="sale.view_order_tree"/>
<field name="arch" type="xml">
<field name="state" position="before">
<field name="is_weighted"/>
</field>
</field>
</record>
<record id="sale_order_view_search_inherit_sale" model="ir.ui.view">
<field name="name">sale.order.search</field>
<field name="model">sale.order</field>
<field name="inherit_id" ref="sale.sale_order_view_search_inherit_sale"/>
<field name="arch" type="xml">
<filter name="sales" position="before">
<filter string="Non ventilé" name="is_weighted" domain="[('is_weighted','=',False)]"/>
<separator/>
</filter>
</field>
</record>
</data>
</odoo>
\ No newline at end of file
......@@ -5,12 +5,13 @@
<field name="name">sale.order.pcafae.line.tree</field>
<field name="model">sale.order.pcafae.line</field>
<field name="arch" type="xml">
<tree string="Pondération Commandes" create="false" edit="false" delete="false" >
<tree string="Prévisionnel" create="false" edit="false" delete="false" >
<field name="year"/>
<field name="partner_id"/>
<field name="order_id"/>
<field name="order_id" widget="many2one_clickable"/>
<field name="year_percent"/>
<field name="year_amount"/>
<field name="year_amount" widget="monetary"/>
<field name="company_currency_id" invisible="1"/>
<field name="date"/>
</tree>
</field>
......@@ -20,7 +21,7 @@
<field name="name">sale.order.pcafae.line.search</field>
<field name="model">sale.order.pcafae.line</field>
<field name="arch" type="xml">
<search string="Pondération Commandes">
<search string="Prévisionnel">
<field name="year"/>
<field name="partner_id"/>
<field name="order_id"/>
......@@ -32,9 +33,9 @@
<field name="name">sale.order.pcafae.line.pivot</field>
<field name="model">sale.order.pcafae.line</field>
<field name="arch" type="xml">
<pivot string="Pondération Commandes">
<pivot string="Prévisionnel">
<field name="year" type="row"/>
<field name="year_amount" type="measure"/>
<field name="year_amount" type="measure" widget="monetary"/>
</pivot>
</field>
</record>
......@@ -43,7 +44,7 @@
<field name="name">sale.order.pcafae.line.graph</field>
<field name="model">sale.order.pcafae.line</field>
<field name="arch" type="xml">
<graph string="Pondération Commandes">
<graph string="Prévisionnels">
<field name="year"/>
<field name="year_amount" type="measure"/>
</graph>
......@@ -51,11 +52,11 @@
</record>
<record id="sale_order_pcafae_line_act_window" model="ir.actions.act_window">
<field name="name">Pondération Commandes</field>
<field name="name">Prévisionnel</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">sale.order.pcafae.line</field>
<field name="view_type">form</field>
<field name="view_mode">tree,pivot,graph</field>
<field name="view_mode">pivot,tree,graph</field>
<field name="domain" eval="[('state', 'in', ('sale', 'done'))]"/>
</record>
</data>
......
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