diff --git a/__manifest__.py b/__manifest__.py index 0157de5b7f93f95f90383d7eac2cdb8bc894f9ee..be58a74bcc5cf479b17cd7f6d2d74c676fe73a22 100644 --- a/__manifest__.py +++ b/__manifest__.py @@ -19,6 +19,7 @@ "views/sale_project_subvention_views.xml", "views/sale_project_view.xml", "views/sale_intervention_view.xml", + "views/sale_intervention_stock_view.xml", "views/sale_project_admin_state_views.xml", "views/product_template_views.xml", "views/res_partner_views.xml", diff --git a/models/__init__.py b/models/__init__.py index cc1f80f103e9b7707adfa54d7d011f4b684792ad..7d875aaf756e678d9f95fb0d2a70354794ca6f47 100644 --- a/models/__init__.py +++ b/models/__init__.py @@ -8,5 +8,6 @@ from . import sale_order_line from . import sale_project from . import sale_intervention_plant_sequence from . import sale_intervention +from . import sale_intervention_stock from . import res_partner from . import product_category diff --git a/models/sale_intervention.py b/models/sale_intervention.py index ab4e5542b917a6492b3d6d24e5b33c8d3116ead1..7974887830a407cbe154e6b310e0ddaa1fba011c 100644 --- a/models/sale_intervention.py +++ b/models/sale_intervention.py @@ -1,4 +1,4 @@ -# Copyright 2021 Le Filament (https://le-filament.com) +# Copyright 2021-2022 Le Filament (https://le-filament.com) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) import math @@ -979,52 +979,3 @@ class SaleIntervention(models.Model): res = super().unlink() project_id.update_order_lines() return res - - -class SaleInterventionStock(models.Model): - _name = "sale.intervention.stock" - _description = "Stock d'interventions'" - _order = "sale_order_id, project_id, categ_id, product_id" - - sale_intervention_id = fields.Many2one( - comodel_name="sale.intervention", - string="Intervention", - ondelete="cascade", - required=True, - ) - project_id = fields.Many2one( - related="sale_intervention_id.project_id", string="Projet", store=True - ) - sale_order_id = fields.Many2one( - related="project_id.sale_order_id", - string="Devis/Commande", - store=True, - ) - product_id = fields.Many2one( - "product.product", - string="Product", - required=True, - change_default=True, - ondelete="restrict", - ) - product_template_id = fields.Many2one(related="product_id.product_tmpl_id") - name = fields.Char(string="Article", related="product_id.name") - categ_id = fields.Many2one(related="product_template_id.categ_id", store=True) - product_uom_qty = fields.Float( - string="Quantité", digits="Product Unit of Measure", required=True, default=1.0 - ) - price_unit = fields.Float( - "Prix Unitaire", required=True, digits="Product Price", default=0.0 - ) - price_total = fields.Float( - compute="_compute_amount", - string="Total", - readonly=True, - store=True, - group_operator="sum", - ) - - @api.depends("product_uom_qty", "price_unit") - def _compute_amount(self): - for rec in self: - rec.price_total = rec.product_uom_qty * rec.price_unit diff --git a/models/sale_intervention_stock.py b/models/sale_intervention_stock.py new file mode 100644 index 0000000000000000000000000000000000000000..3ab72a773f40fc10a9ae2b1d6410bc223d3f9f30 --- /dev/null +++ b/models/sale_intervention_stock.py @@ -0,0 +1,73 @@ +# Copyright 2022 Le Filament (https://le-filament.com) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) + +from odoo import api, fields, models + + +class SaleInterventionStock(models.Model): + _name = "sale.intervention.stock" + _description = "Stock d'interventions'" + _order = "sale_order_id, project_id, categ_id, product_id" + + sale_intervention_id = fields.Many2one( + comodel_name="sale.intervention", + string="Intervention", + ondelete="cascade", + required=True, + ) + + # Related fields for display / analysis + intervention_type_id = fields.Many2one( + related="sale_intervention_id.intervention_type_id", + string="Type d'intervention", + store=True, + ) + project_id = fields.Many2one( + related="sale_intervention_id.project_id", string="Projet", store=True + ) + saison_id = fields.Many2one( + related="project_id.saison_id", string="Saison", store=True + ) + partner_id = fields.Many2one( + related="project_id.partner_id", string="Client", store=True + ) + user_id = fields.Many2one( + related="project_id.user_id", string="Référent", store=True + ) + admin_state_id = fields.Many2one( + related="project_id.admin_state_id", string="Étape du projet", store=True + ) + + sale_order_id = fields.Many2one( + related="project_id.sale_order_id", + string="Devis/Commande", + store=True, + ) + product_id = fields.Many2one( + "product.product", + string="Product", + required=True, + change_default=True, + ondelete="restrict", + ) + product_template_id = fields.Many2one(related="product_id.product_tmpl_id") + name = fields.Char(string="Article", related="product_id.name") + categ_id = fields.Many2one(related="product_template_id.categ_id", store=True) + product_uom_qty = fields.Float( + string="Quantité", digits="Product Unit of Measure", required=True, default=1.0 + ) + price_unit = fields.Float( + "Prix Unitaire", required=True, digits="Product Price", default=0.0 + ) + price_total = fields.Float( + compute="_compute_amount", + string="Prix Total", + readonly=True, + store=True, + group_operator="sum", + ) + + @api.depends("product_uom_qty", "price_unit") + def _compute_amount(self): + for rec in self: + rec.price_total = rec.product_uom_qty * rec.price_unit diff --git a/views/sale_intervention_stock_view.xml b/views/sale_intervention_stock_view.xml new file mode 100644 index 0000000000000000000000000000000000000000..6c857cb429d80371538d768799a57149a952bb3c --- /dev/null +++ b/views/sale_intervention_stock_view.xml @@ -0,0 +1,142 @@ +<?xml version="1.0" encoding="utf-8" ?> +<!-- Copyright 2021 Le Filament + License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). --> +<odoo> + <data> + + <record model="ir.ui.view" id="sale_intervention_stock_tree"> + <field name="name">Tree view for sale intervention stock</field> + <field name="model">sale.intervention.stock</field> + <field name="arch" type="xml"> + <tree + string="Déstockage Interventions" + edit="false" + create="false" + delete="false" + import="false" + > + <field name="name" /> + <field name="project_id" optional="show" /> + <field name="saison_id" optional="show" /> + <field name="partner_id" optional="show" /> + <field name="user_id" optional="show" /> + <field name="admin_state_id" optional="show" /> + <field name="sale_intervention_id" optional="hide" /> + <field name="intervention_type_id" optional="hide" /> + <field name="sale_order_id" optional="hide" /> + <field name="categ_id" optional="hide" /> + <field name="product_uom_qty" sum="Total" /> + <field name="price_unit" optional="hide" /> + <field name="price_total" sum="Total" optional="hide" /> + </tree> + </field> + </record> + <record model="ir.ui.view" id="sale_intervention_stock_search"> + <field name="name">Search view for sale intervention stock</field> + <field name="model">sale.intervention.stock</field> + <field name="arch" type="xml"> + <search string="Recherche de Déstockage d'Interventions"> + <field name="product_id" /> + <field name="partner_id" /> + <field name="project_id" /> + <field name="user_id" /> + <field name="categ_id" /> + <group expand="0" string="Group By"> + <filter + name="group_by_project" + string="Projet" + domain="[]" + context="{'group_by':'project_id'}" + /> + <filter + name="group_by_partner" + string="Client" + domain="[]" + context="{'group_by':'partner_id'}" + /> + <filter + name="group_by_saison" + string="Saison" + domain="[]" + context="{'group_by':'saison_id'}" + /> + <filter + name="group_by_admin_state_id" + string="Étape de Projet" + domain="[]" + context="{'group_by':'admin_state_id'}" + /> + <filter + name="group_by_user" + string="Référent" + domain="[]" + context="{'group_by':'user_id'}" + /> + <filter + name="group_by_product_template_id" + string="Article" + domain="[]" + context="{'group_by':'product_id'}" + /> + <filter + name="group_by_intervention" + string="Intervention" + domain="[]" + context="{'group_by':'sale_intervention_id'}" + /> + <filter + name="group_by_intervention_type" + string="Type d'intervention" + domain="[]" + context="{'group_by':'intervention_type_id'}" + /> + </group> + </search> + </field> + </record> + <record model="ir.ui.view" id="sale_intervention_stock_pivot"> + <field name="name">Pivot view for sale intervention stock</field> + <field name="model">sale.intervention.stock</field> + <field name="arch" type="xml"> + <pivot> + <field name="product_id" type="col" /> + <field name="project_id" type="row" /> + <field name="product_uom_qty" type="measure" /> + <field name="price_total" type="measure" /> + </pivot> + </field> + </record> + <record model="ir.ui.view" id="sale_intervention_stock_graph"> + <field name="name">Graph view for sale intervention stock</field> + <field name="model">sale.intervention.stock</field> + <field name="arch" type="xml"> + <graph> + <field name="product_id" type="row" /> + <field name="product_uom_qty" type="measure" /> + </graph> + </field> + </record> + + <record model="ir.actions.act_window" id="action_sale_intervention_stock"> + <field name="name">Déstockage Interventions</field> + <field name="res_model">sale.intervention.stock</field> + <field name="view_mode">tree,pivot,graph</field> + <field name="search_view_id" ref="sale_intervention_stock_search" /> + <field name="context">{"search_default_group_by_partner": True}</field> + <field name="help" type="html"> + <p class="o_view_nocontent_empty_folder"> + Aucun article à déstocker trouvé. Créez un nouveau projet depuis un devis + et associez lui des interventions. + </p> + </field> + </record> + <menuitem + id="menu_sale_intervention_stock" + parent="menu_sale_project_root" + name="Déstockage Interventions" + action="action_sale_intervention_stock" + sequence="30" + /> + + </data> +</odoo>