dawn js
This commit is contained in:
parent
f3a7072314
commit
b06e9c4569
6 changed files with 284 additions and 178 deletions
|
|
@ -1,10 +1,10 @@
|
|||
|
||||
{
|
||||
'name': 'Customer Itch Cycle Management',
|
||||
'version': '1.0',
|
||||
'depends': [
|
||||
'base',
|
||||
'sale_stock'
|
||||
'sale_stock',
|
||||
'web'
|
||||
],
|
||||
'author': 'Benoit Vézina',
|
||||
'category': 'Sales Management',
|
||||
|
|
@ -17,8 +17,15 @@
|
|||
'views/res_partner_view.xml',
|
||||
'views/product_category_view.xml',
|
||||
'security/ir.model.access.csv'
|
||||
|
||||
],
|
||||
'assets': {
|
||||
'web.assets_backend': [
|
||||
'customer_itch_cycle/static/src/js/cycle_product_partner_list_view.js',
|
||||
],
|
||||
'web.assets_backend_xml': [
|
||||
'customer_itch_cycle/static/src/xml/process_history_button.xml',
|
||||
],
|
||||
},
|
||||
'installable': True,
|
||||
'application': False,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
from datetime import datetime, timedelta
|
||||
|
||||
import numpy as np
|
||||
from odoo import models, fields, api
|
||||
from odoo.exceptions import UserError
|
||||
|
|
@ -11,167 +12,193 @@ class ItchCycleProductPartner(models.Model):
|
|||
partner_id = fields.Many2one(
|
||||
comodel_name='res.partner',
|
||||
string="Client",
|
||||
help="Client associé à ce cycle",
|
||||
required=True
|
||||
)
|
||||
|
||||
product_id = fields.Many2one(
|
||||
comodel_name='product.product',
|
||||
string="Produit",
|
||||
help="Produit associé à ce cycle",
|
||||
required=True
|
||||
)
|
||||
|
||||
quantity_total_ordered = fields.Float(
|
||||
string="Quantité totale commandée",
|
||||
compute="_compute_quantity_total_ordered",
|
||||
store=True
|
||||
)
|
||||
|
||||
quantity_of_orders = fields.Integer(
|
||||
string="Nombre de commandes",
|
||||
compute="_compute_quantity_of_orders",
|
||||
store=True
|
||||
)
|
||||
|
||||
min_quantity_ordered = fields.Float(
|
||||
string="Quantité minimale commandée",
|
||||
compute="_compute_min_quantity_ordered",
|
||||
store=True
|
||||
)
|
||||
|
||||
max_quantity_ordered = fields.Float(
|
||||
string="Quantité maximale commandée",
|
||||
compute="_compute_max_quantity_ordered",
|
||||
store=True
|
||||
)
|
||||
|
||||
mean_quantity_ordered = fields.Float(
|
||||
string="Quantité moyenne commandée",
|
||||
compute="_compute_mean_quantity_ordered",
|
||||
store=True
|
||||
)
|
||||
|
||||
average_cycle = fields.Integer(
|
||||
string="Cycle moyen (jours)",
|
||||
compute="_compute_average_cycle",
|
||||
store=True
|
||||
)
|
||||
|
||||
manualy_set_cycle = fields.Integer(
|
||||
string="Cycle moyen (jours) défini manuellement"
|
||||
)
|
||||
|
||||
manualy_set_qty = fields.Float(
|
||||
string="Quantité défini manuellement"
|
||||
)
|
||||
|
||||
next_expected_date = fields.Date(
|
||||
string="Prochaine vente prévue",
|
||||
compute="_compute_next_expected_date",
|
||||
store=True
|
||||
)
|
||||
|
||||
manualy_set_next_date = fields.Date(
|
||||
string="Prochaine vente prévue défini manuellement"
|
||||
)
|
||||
|
||||
calc_next_date = fields.Date(
|
||||
string="Prochaine vente prévue calculée",
|
||||
compute="_compute_next_expected_date",
|
||||
store=True
|
||||
)
|
||||
|
||||
calc_average_cycle = fields.Integer(
|
||||
string="Cycle moyen (jours) calculé",
|
||||
compute="_compute_itch_cycle_duration",
|
||||
store=True
|
||||
)
|
||||
|
||||
calc_average_qty = fields.Float(
|
||||
string="Quantité moyenne calculée",
|
||||
compute="_compute_mean_quantity_ordered",
|
||||
store=True
|
||||
)
|
||||
|
||||
@api.depends('manualy_set_cycle','average_cycle')
|
||||
def _compute_itch_cycle_duration(self):
|
||||
for record in self:
|
||||
if record.manualy_set_cycle:
|
||||
record.calc_average_cycle = record.manualy_set_cycle
|
||||
else:
|
||||
record.calc_average_cycle = record.average_cycle
|
||||
|
||||
@api.depends('manualy_set_qty','max_quantity_ordered')
|
||||
def _compute_mean_quantity_ordered(self):
|
||||
for record in self:
|
||||
if record.manualy_set_qty:
|
||||
record.calc_average_qty = record.manualy_set_qty
|
||||
else:
|
||||
record.calc_average_qty = record.max_quantity
|
||||
|
||||
prediction_status = fields.Selection(
|
||||
selection=[
|
||||
('on_time', 'À l’heure'),
|
||||
('delayed', 'Retardée')
|
||||
],
|
||||
string="Statut de la prédiction",
|
||||
compute="_compute_prediction_status"
|
||||
)
|
||||
|
||||
sale_order_line_ids = fields.One2many(
|
||||
comodel_name='sale.order.line',
|
||||
inverse_name='itch_cycle_id',
|
||||
help="Lignes de commande",
|
||||
string="Lignes de commande"
|
||||
)
|
||||
|
||||
next_follow_up_date = fields.Date(
|
||||
# Quantités: La plus part des champs sont informatifs et calculés automatiquement
|
||||
|
||||
quantity_total_ordered = fields.Float(
|
||||
string="Quantité totale commandée",
|
||||
compute="_compute_sale_order_line_related_fields",
|
||||
help="Quantité totale commandée par le client pour ce produit",
|
||||
store=True
|
||||
)
|
||||
|
||||
quantity_of_orders = fields.Integer(
|
||||
string="Nombre de commandes",
|
||||
compute="_compute_sale_order_line_related_fields",
|
||||
help="Nombre de commandes passées par le client pour ce produit",
|
||||
store=True
|
||||
)
|
||||
|
||||
quantity_min_ordered = fields.Float(
|
||||
string="Quantité minimale commandée",
|
||||
compute="_compute_sale_order_line_related_fields",
|
||||
help="Quantité minimale commandée par le client pour ce produit",
|
||||
store=True
|
||||
)
|
||||
|
||||
quantity_max_ordered = fields.Float(
|
||||
string="Quantité maximale commandée",
|
||||
compute="_compute_sale_order_line_related_fields",
|
||||
help="Quantité maximale commandée par le client pour ce produit",
|
||||
store=True
|
||||
)
|
||||
|
||||
quantity_mean_ordered = fields.Float(
|
||||
string="Quantité moyenne commandée",
|
||||
compute="_compute_sale_order_line_related_fields",
|
||||
help="Quantité moyenne commandée par le client pour ce produit",
|
||||
store=True
|
||||
)
|
||||
|
||||
quantity_manual_override = fields.Float(
|
||||
string="Quantité défini manuellement",
|
||||
help="Quantité défini manuellement"
|
||||
)
|
||||
|
||||
quantity_planned = fields.Float(
|
||||
string="Quantité prévue",
|
||||
help="Quantité prévue pour la prochaine commande",
|
||||
compute="_compute_quantity_planned",
|
||||
store=True
|
||||
)
|
||||
|
||||
# Cycles: La plupart des champs sont informatifs et calculés automatiquement
|
||||
|
||||
cycle_duration_calculated = fields.Integer(
|
||||
string="Cycle moyen (jours) calculé",
|
||||
compute="_compute_average_cycle",
|
||||
help="Cycle moyen calculé en jours",
|
||||
store=True
|
||||
)
|
||||
|
||||
cycle_duration_override = fields.Integer(
|
||||
string="Cycle moyen (jours) défini manuellement",
|
||||
help="Cycle moyen défini manuellement en jours"
|
||||
)
|
||||
|
||||
cycle_duration = fields.Integer(
|
||||
string="Cycle moyen (jours)",
|
||||
compute="_compute_itch_cycle_duration",
|
||||
help="Cycle moyen en jours",
|
||||
store=True
|
||||
)
|
||||
|
||||
# Dates:
|
||||
|
||||
date_expected_evaluated = fields.Date(
|
||||
string="Prochaine vente prévue par calcul",
|
||||
compute="_compute_next_expected_date",
|
||||
store=True
|
||||
)
|
||||
|
||||
date_next_override = fields.Date(
|
||||
string="Prochaine vente prévue défini manuellement",
|
||||
help="Prochaine vente prévue défini manuellement"
|
||||
)
|
||||
|
||||
date_expected = fields.Date(
|
||||
string="Prochaine vente prévue",
|
||||
compute="_compute_next_expected_date",
|
||||
help="Prochaine vente prévue calculée",
|
||||
store=True
|
||||
)
|
||||
|
||||
date_next_follow_up = fields.Date(
|
||||
string="Prochaine date de suivi",
|
||||
compute="_compute_next_follow_up_date",
|
||||
help="Prochaine date de suivi",
|
||||
store=True,
|
||||
readonly=False
|
||||
)
|
||||
|
||||
last_purchase_date = fields.Date(
|
||||
date_last_purchase = fields.Date(
|
||||
string="Date du dernier achat",
|
||||
compute="_compute_last_purchase_date",
|
||||
help="Date du dernier achat",
|
||||
store=True
|
||||
)
|
||||
|
||||
itch_cycle_duration = fields.Integer(
|
||||
string="Durée du cycle (jours)",
|
||||
help="Représente la durée moyenne du cycle en jours",
|
||||
compute="_compute_itch_cycle_duration",
|
||||
store=True
|
||||
prediction_status = fields.Selection(
|
||||
selection=[
|
||||
('on_time', 'À l’heure'),
|
||||
('delayed', 'Retardée'),
|
||||
('archived', 'Archivée')
|
||||
],
|
||||
string="Statut de la prédiction",
|
||||
help="Statut de la prédiction",
|
||||
compute="_compute_prediction_status"
|
||||
)
|
||||
|
||||
@api.depends('average_cycle')
|
||||
@api.depends('cycle_duration_override', 'cycle_duration_calculated')
|
||||
def _compute_itch_cycle_duration(self):
|
||||
"""Calcule automatiquement la durée du cycle."""
|
||||
"""
|
||||
Calcule automatiquement la durée du cycle.
|
||||
Si un cycle est défini manuellement, il est utilisé à la place.
|
||||
Sinon, le cycle moyen calculé est utilisé.
|
||||
"""
|
||||
for record in self:
|
||||
record.itch_cycle_duration = record.average_cycle or 0
|
||||
if record.cycle_duration_override:
|
||||
record.cycle_duration = record.cycle_duration_override
|
||||
else:
|
||||
record.cycle_duration = record.cycle_duration_calculated
|
||||
|
||||
@api.depends('quantity_manual_override', 'quantity_max_ordered')
|
||||
def _compute_mean_quantity_ordered(self):
|
||||
"""
|
||||
Calcule la quantité de la prochaine commande.
|
||||
Si une quantité est définie manuellement, elle est utilisée sinon la quantité maximale est utilisée.
|
||||
"""
|
||||
for record in self:
|
||||
if record.quantity_manual_override:
|
||||
record.calc_average_qty = record.quantity_manual_override
|
||||
else:
|
||||
record.calc_average_qty = record.quantity_max_ordered
|
||||
|
||||
@api.depends('sale_order_line_ids')
|
||||
def _compute_last_purchase_date(self):
|
||||
"""Met automatiquement à jour la date du dernier achat."""
|
||||
"""
|
||||
Met automatiquement à jour la date du dernier achat.
|
||||
"""
|
||||
for record in self:
|
||||
# Rechercher la commande la plus récente associée
|
||||
last_order = record.sale_order_line_ids.mapped('order_id').sorted(key=lambda o: o.date_order, reverse=True)
|
||||
record.last_purchase_date = last_order[0].date_order if last_order else None
|
||||
record.date_last_purchase = last_order[0].date_order if last_order else None
|
||||
|
||||
@api.depends('next_expected_date')
|
||||
@api.depends('date_expected')
|
||||
def _compute_next_follow_up_date(self):
|
||||
"""
|
||||
Définit la logique pour calculer automatiquement la date de suivi.
|
||||
Peut être basée sur `next_expected_date`, mais modifiable par l'utilisateur.
|
||||
"""
|
||||
Définit la logique pour calculer automatiquement la date de suivi.
|
||||
Peut être basée sur `next_expected_date`, mais modifiable par l'utilisateur.
|
||||
"""
|
||||
for record in self:
|
||||
if record.next_expected_date:
|
||||
if record.date_expected:
|
||||
# Exemple : une alerte de suivi une semaine avant la prochaine vente prévue
|
||||
record.next_follow_up_date = record.next_expected_date - timedelta(days=7)
|
||||
record.date_next_follow_up = record.date_expected - timedelta(days=7)
|
||||
else:
|
||||
record.next_follow_up_date = None
|
||||
record.date_next_follow_up = None
|
||||
|
||||
@api.depends('sale_order_line_ids', 'product_id.categ_id')
|
||||
def _compute_average_cycle(self):
|
||||
"""
|
||||
Calcule le cycle moyen en jours.
|
||||
"""
|
||||
for record in self:
|
||||
product_category = record.product_id.categ_id
|
||||
if product_category.seasonal_factor and product_category.season_months:
|
||||
|
|
@ -188,23 +215,31 @@ class ItchCycleProductPartner(models.Model):
|
|||
|
||||
if len(dates) > 1:
|
||||
intervals = [(dates[i + 1] - dates[i]).days for i in range(len(dates) - 1)]
|
||||
record.average_cycle = int(np.mean(intervals)) if intervals else 0
|
||||
record.cycle_duration_calculated = int(np.mean(intervals)) if intervals else 0
|
||||
else:
|
||||
record.average_cycle = 0
|
||||
record.cycle_duration_calculated = 0
|
||||
|
||||
@api.depends('average_cycle')
|
||||
@api.depends('cycle_duration')
|
||||
def _compute_next_expected_date(self):
|
||||
"""
|
||||
Calcule automatiquement la prochaine date de vente
|
||||
"""
|
||||
for record in self:
|
||||
if record.average_cycle and record.sale_order_line_ids:
|
||||
if record.cycle_duration and record.sale_order_line_ids:
|
||||
last_date = max(record.sale_order_line_ids.mapped('order_id.date_order'))
|
||||
record.next_expected_date = fields.Date.from_string(last_date) + timedelta(days=record.average_cycle)
|
||||
record.date_expected_evaluated = fields.Date.from_string(last_date) + timedelta(days=record.cycle_duration)
|
||||
else:
|
||||
record.next_expected_date = None
|
||||
record.date_expected_evaluated = None
|
||||
|
||||
def _compute_prediction_status(self):
|
||||
"""
|
||||
Calcule le statut de la prédiction.
|
||||
"""
|
||||
today = fields.Date.context_today(self)
|
||||
for record in self:
|
||||
if record.next_expected_date and record.next_expected_date < today:
|
||||
if record.prediction_status == 'archived':
|
||||
record.prediction_status = 'archived'
|
||||
elif record.date_expected and record.date_expected < today:
|
||||
record.prediction_status = 'delayed'
|
||||
else:
|
||||
record.prediction_status = 'on_time'
|
||||
|
|
@ -221,7 +256,6 @@ class ItchCycleProductPartner(models.Model):
|
|||
('order_id.state', 'in', ['sale', 'done']), # Commandes confirmées ou terminées
|
||||
('qty_delivered', '>', 0), # Quantité livrée supérieure à 0
|
||||
('product_id', '!=', False) # Exclure les lignes sans produit
|
||||
# ('order_id.partner_id', '=', 5024659), # un client pour test
|
||||
])
|
||||
|
||||
# Carte pour regrouper par couple (client, produit)
|
||||
|
|
@ -262,8 +296,8 @@ class ItchCycleProductPartner(models.Model):
|
|||
'sale_order_line_ids': [(6, 0, data['sale_order_line_ids'])]
|
||||
})
|
||||
else:
|
||||
#raise UserError("Impossible de créer un cycle sans client ou produit associé.")
|
||||
print("Impossible de créer un cycle sans client ou produit associé.")
|
||||
raise UserError('Impossible de créer un cycle sans client ou produit associé.')
|
||||
print('Impossible de créer un cycle sans client ou produit associé.')
|
||||
print('valeur de product_id', product_id)
|
||||
print('valeur de partner_id', partner_id)
|
||||
else:
|
||||
|
|
@ -274,26 +308,42 @@ class ItchCycleProductPartner(models.Model):
|
|||
return True
|
||||
|
||||
@api.depends('sale_order_line_ids')
|
||||
def _compute_quantity_total_ordered(self):
|
||||
def _compute_sale_order_line_related_fields(self):
|
||||
"""
|
||||
Regroupe tous les calculs relatifs aux `sale.order.line`.
|
||||
Calcule les statistiques principales : quantité totale, moyenne, minimum, maximum, etc.
|
||||
"""
|
||||
for record in self:
|
||||
record.quantity_total_ordered = sum(record.sale_order_line_ids.mapped('product_uom_qty'))
|
||||
# Récupérer toutes les quantités des lignes de commande
|
||||
quantities = record.sale_order_line_ids.mapped('product_uom_qty')
|
||||
|
||||
@api.depends('sale_order_line_ids')
|
||||
def _compute_quantity_of_orders(self):
|
||||
for record in self:
|
||||
# Quantité totale
|
||||
record.quantity_total_ordered = sum(quantities)
|
||||
|
||||
# Nombre de commandes (lignes individuelles)
|
||||
record.quantity_of_orders = len(record.sale_order_line_ids)
|
||||
|
||||
@api.depends('sale_order_line_ids')
|
||||
def _compute_min_quantity_ordered(self):
|
||||
for record in self:
|
||||
record.min_quantity_ordered = min(record.sale_order_line_ids.mapped('product_uom_qty'))
|
||||
# Quantité minimale, maximale et moyenne
|
||||
if quantities:
|
||||
record.quantity_min_ordered = min(quantities)
|
||||
record.quantity_max_ordered = max(quantities)
|
||||
record.quantity_mean_ordered = np.mean(quantities)
|
||||
else:
|
||||
record.quantity_min_ordered = 0
|
||||
record.quantity_max_ordered = 0
|
||||
record.quantity_mean_ordered = 0
|
||||
|
||||
@api.depends('sale_order_line_ids')
|
||||
def _compute_max_quantity_ordered(self):
|
||||
for record in self:
|
||||
record.max_quantity_ordered = max(record.sale_order_line_ids.mapped('product_uom_qty'))
|
||||
# Dernière date d'achat (commande la plus récente)
|
||||
last_order = record.sale_order_line_ids.mapped('order_id').sorted(key=lambda o: o.date_order, reverse=True)
|
||||
record.date_last_purchase = last_order[0].date_order if last_order else None
|
||||
|
||||
@api.depends('sale_order_line_ids')
|
||||
def _compute_mean_quantity_ordered(self):
|
||||
@api.depends('quantity_manual_override', 'quantity_mean_ordered')
|
||||
def _compute_quantity_planned(self):
|
||||
"""
|
||||
Calcule la quantité prévue pour la prochaine commande.
|
||||
"""
|
||||
for record in self:
|
||||
record.mean_quantity_ordered = np.mean(record.sale_order_line_ids.mapped('product_uom_qty'))
|
||||
if record.quantity_manual_override:
|
||||
record.quantity_planned = record.quantity_manual_override
|
||||
else:
|
||||
record.quantity_planned = record.quantity_mean_ordered
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
/** @odoo-module **/
|
||||
|
||||
import { registry } from "@web/core/registry";
|
||||
import { listView } from "@web/views/list/list_view";
|
||||
import { ListController } from "@web/views/list/list_controller";
|
||||
|
||||
class CycleProductPartnerListController extends ListController {
|
||||
setup() {
|
||||
super.setup();
|
||||
this.orm = this.env.services.orm; // Service ORM pour effectuer des appels RPC
|
||||
}
|
||||
|
||||
async ProcessHistoryButton() {
|
||||
try {
|
||||
// Appel RPC via le service ORM
|
||||
await this.orm.call("itch.cycle.product.partner", "populate_from_past_orders", []);
|
||||
this.displayNotification({
|
||||
type: "success",
|
||||
message: "L'action a été exécutée avec succès.",
|
||||
});
|
||||
} catch (error) {
|
||||
this.displayNotification({
|
||||
type: "danger",
|
||||
message: `Une erreur s'est produite : ${error.message}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Enregistrement de la vue personnalisée
|
||||
registry.category("views").add("process_history_button_in_tree", {
|
||||
...listView,
|
||||
Controller: CycleProductPartnerListController,
|
||||
buttonTemplate: "customer_itch_cycle.button_in_tree",
|
||||
});
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<template id="process_history_button_in_tree" name="Button in Tree">
|
||||
<t t-call="web.Button" class="btn-primary" t-on-click="ProcessHistoryButton">
|
||||
<t t-esc="__('Mettre à jour depuis commandes passées')"/>
|
||||
</t>
|
||||
</template>
|
||||
|
|
@ -1,16 +1,17 @@
|
|||
<odoo>
|
||||
<record id="view_itch_cycle_product_partner_tree" model="ir.ui.view">
|
||||
<record id="view_cycle_product_partner_tree" model="ir.ui.view">
|
||||
<field name="name">itch.cycle.product.partner.tree</field>
|
||||
<field name="model">itch.cycle.product.partner</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree>
|
||||
<tree js_class="cycle_product_partner_list">
|
||||
<field name="partner_id"/>
|
||||
<field name="product_id"/>
|
||||
<field name="itch_cycle_duration"/>
|
||||
<field name="average_cycle"/>
|
||||
<field name="last_purchase_date"/>
|
||||
<field name="next_expected_date"/>
|
||||
<field name="next_follow_up_date"/>
|
||||
<field name="quantity_planned"/>
|
||||
<field name="cycle_duration"/>
|
||||
<field name="cycle_duration_calculated"/>
|
||||
<field name="date_last_purchase"/>
|
||||
<field name="date_expected"/>
|
||||
<field name="date_next_follow_up"/>
|
||||
<field name="prediction_status"/>
|
||||
</tree>
|
||||
</field>
|
||||
|
|
@ -22,7 +23,7 @@
|
|||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<header>
|
||||
<field name="prediction_status" widget="statusbar" options="{'on_time': 'green', 'delayed': 'red'}"/>
|
||||
<field name="prediction_status" widget="statusbar" options="{'on_time': 'green', 'delayed': 'red', 'archived': 'gray'}"/>
|
||||
</header>
|
||||
<sheet>
|
||||
<group>
|
||||
|
|
@ -30,22 +31,31 @@
|
|||
<field name="partner_id"/>
|
||||
<field name="product_id"/>
|
||||
<group string="Surchage manuelle">
|
||||
<field name="manualy_set_cycle"/>
|
||||
<field name="manualy_set_qty"/>
|
||||
<field name="manualy_set_next_date"/>
|
||||
<field name="cycle_duration_override"/>
|
||||
<field name="quantity_manual_override"/>
|
||||
<field name="date_next_override"/>
|
||||
</group>
|
||||
<group string="Statut">
|
||||
<field name="date_expected" readonly="1"/>
|
||||
<field name="cycle_duration" readonly="1"/>
|
||||
<field name="quantity_planned" readonly="1"/>
|
||||
<field name="prediction_status" readonly="1"/>
|
||||
</group>
|
||||
</group>
|
||||
<group string="Statistiques">
|
||||
<field name="itch_cycle_duration" readonly="1"/>
|
||||
<field name="average_cycle" readonly="1"/>
|
||||
<field name="last_purchase_date" readonly="1"/>
|
||||
<field name="next_expected_date" readonly="1"/>
|
||||
<field name="next_follow_up_date"/>
|
||||
<field name="quantity_of_orders" readonly="1"/>
|
||||
<field name="quantity_total_ordered" readonly="1"/>
|
||||
<field name="min_quantity_ordered" readonly="1"/>
|
||||
<field name="max_quantity_ordered" readonly="1"/>
|
||||
<field name="mean_quantity_ordered" readonly="1"/>
|
||||
<group string="Statistiques de commandes">
|
||||
<field name="quantity_of_orders" readonly="1"/>
|
||||
<field name="quantity_total_ordered" readonly="1"/>
|
||||
<field name="quantity_min_ordered" readonly="1"/>
|
||||
<field name="quantity_max_ordered" readonly="1"/>
|
||||
<field name="quantity_mean_ordered" readonly="1"/>
|
||||
</group>
|
||||
<group string="Statistiques de cycle">
|
||||
<field name="date_last_purchase" readonly="1"/>
|
||||
<field name="date_next_follow_up" readonly="1"/>
|
||||
<field name="cycle_duration_calculated" readonly="1"/>
|
||||
<field name="date_expected_evaluated" readonly="1"/>
|
||||
</group>
|
||||
</group>
|
||||
</group>
|
||||
<notebook>
|
||||
|
|
@ -67,16 +77,17 @@
|
|||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_itch_cycle_product_partner" model="ir.actions.act_window">
|
||||
<field name="name">Itch Cycles Produits</field>
|
||||
<record id="action_cycle_product_partner" model="ir.actions.act_window">
|
||||
<field name="name">Product Cycle Management</field>
|
||||
<field name="res_model">itch.cycle.product.partner</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="view_id" ref="view_itch_cycle_product_partner_tree"/>
|
||||
<field name="view_id" ref="view_cycle_product_partner_tree"/>
|
||||
</record>
|
||||
|
||||
<menuitem id="menu_itch_cycle_product_root"
|
||||
name="Itch Cycle"
|
||||
sequence="10"
|
||||
name="Product Cycle"
|
||||
|
||||
sequence="5"
|
||||
parent="sale.menu_sale_report"
|
||||
action="action_itch_cycle_product_partner"/>
|
||||
action="action_cycle_product_partner"/>
|
||||
</odoo>
|
||||
|
|
@ -17,12 +17,10 @@
|
|||
<field name="reorder_cycle_product_ids" readonly="1">
|
||||
<tree>
|
||||
<field name="product_id"/>
|
||||
<field name="last_purchase_date"/>
|
||||
<field name="itch_cycle_duration"/>
|
||||
<field name="next_follow_up_date"/>
|
||||
<field name="min_quantity_ordered"/>
|
||||
<field name="mean_quantity_ordered"/>
|
||||
<field name="max_quantity_ordered"/>
|
||||
<field name="date_last_purchase"/>
|
||||
<field name="cycle_duration"/>
|
||||
<field name="date_next_follow_up"/>
|
||||
<field name="quantity_planned"/>
|
||||
<field name="quantity_of_orders"/>
|
||||
<field name="quantity_total_ordered"/>
|
||||
</tree>
|
||||
|
|
|
|||
Loading…
Reference in a new issue