update
This commit is contained in:
parent
5a97745d42
commit
9578594628
13 changed files with 361 additions and 102 deletions
|
|
@ -9,6 +9,9 @@
|
||||||
'data': [
|
'data': [
|
||||||
'views/itch_cycle_product_partner_view.xml',
|
'views/itch_cycle_product_partner_view.xml',
|
||||||
'views/res_partner_view.xml',
|
'views/res_partner_view.xml',
|
||||||
|
'views/product_category_view.xml',
|
||||||
|
'security/ir.model.access.csv'
|
||||||
|
|
||||||
],
|
],
|
||||||
'installable': True,
|
'installable': True,
|
||||||
'application': False,
|
'application': False,
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
|
||||||
from . import itch_cycle_product_partner
|
from . import itch_cycle_product_partner
|
||||||
from . import sale_order
|
from . import sale_order_line
|
||||||
|
from . import product_category
|
||||||
|
from . import res_partner
|
||||||
|
|
|
||||||
|
|
@ -1,33 +1,195 @@
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
import numpy as np
|
||||||
from odoo import models, fields, api
|
from odoo import models, fields, api
|
||||||
from datetime import timedelta, datetime
|
from odoo.exceptions import UserError
|
||||||
|
|
||||||
|
|
||||||
class ItchCycleProductPartner(models.Model):
|
class ItchCycleProductPartner(models.Model):
|
||||||
_name = 'itch_cycle_product_partner'
|
_name = "itch.cycle.product.partner"
|
||||||
_description = 'Itch Cycle by Product and Partner'
|
_description = "Cycle Produit/Partenaire"
|
||||||
|
|
||||||
partner_id = fields.Many2one('res.partner', string="Client", required=True, ondelete='cascade')
|
partner_id = fields.Many2one(
|
||||||
product_id = fields.Many2one('product.product', string="Produit", required=True)
|
comodel_name='res.partner',
|
||||||
last_purchase_date = fields.Date(string="Dernière Date d'Achat")
|
string="Client",
|
||||||
itch_cycle_duration = fields.Integer(string="Durée du Itch-Cycle (jours)", default=0)
|
required=True
|
||||||
next_follow_up_date = fields.Date(string="Prochaine Date de Suivi", compute="_compute_next_follow_up_date", store=True)
|
)
|
||||||
|
|
||||||
@api.depends('last_purchase_date', 'itch_cycle_duration')
|
product_id = fields.Many2one(
|
||||||
def _compute_next_follow_up_date(self):
|
comodel_name='product.product',
|
||||||
|
string="Produit",
|
||||||
|
required=True
|
||||||
|
)
|
||||||
|
|
||||||
|
average_cycle = fields.Integer(
|
||||||
|
string="Cycle moyen (jours)",
|
||||||
|
compute="_compute_average_cycle",
|
||||||
|
store=True
|
||||||
|
)
|
||||||
|
|
||||||
|
next_expected_date = fields.Date(
|
||||||
|
string="Prochaine vente prévue",
|
||||||
|
compute="_compute_next_expected_date",
|
||||||
|
store=True
|
||||||
|
)
|
||||||
|
|
||||||
|
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',
|
||||||
|
string="Lignes de commande"
|
||||||
|
)
|
||||||
|
|
||||||
|
next_follow_up_date = fields.Date(
|
||||||
|
string="Prochaine date de suivi",
|
||||||
|
compute="_compute_next_follow_up_date",
|
||||||
|
store=True,
|
||||||
|
readonly=False
|
||||||
|
)
|
||||||
|
|
||||||
|
last_purchase_date = fields.Date(
|
||||||
|
string="Date du dernier achat",
|
||||||
|
compute="_compute_last_purchase_date",
|
||||||
|
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
|
||||||
|
)
|
||||||
|
|
||||||
|
@api.depends('average_cycle')
|
||||||
|
def _compute_itch_cycle_duration(self):
|
||||||
|
"""Calcule automatiquement la durée du cycle."""
|
||||||
for record in self:
|
for record in self:
|
||||||
if record.last_purchase_date and record.itch_cycle_duration > 0:
|
record.itch_cycle_duration = record.average_cycle or 0
|
||||||
record.next_follow_up_date = record.last_purchase_date + timedelta(days=record.itch_cycle_duration)
|
|
||||||
|
@api.depends('sale_order_line_ids')
|
||||||
|
def _compute_last_purchase_date(self):
|
||||||
|
"""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
|
||||||
|
|
||||||
|
@api.depends('next_expected_date')
|
||||||
|
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.
|
||||||
|
"""
|
||||||
|
for record in self:
|
||||||
|
if record.next_expected_date:
|
||||||
|
# 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)
|
||||||
else:
|
else:
|
||||||
record.next_follow_up_date = False
|
record.next_follow_up_date = None
|
||||||
|
|
||||||
class ResPartner(models.Model):
|
@api.depends('sale_order_line_ids', 'product_id.categ_id')
|
||||||
_inherit = 'res.partner'
|
def _compute_average_cycle(self):
|
||||||
|
for record in self:
|
||||||
|
product_category = record.product_id.categ_id
|
||||||
|
if product_category.seasonal_factor and product_category.season_months:
|
||||||
|
# Filtrer les commandes par mois saisonniers de la catégorie
|
||||||
|
active_months = list(map(int, product_category.season_months.split(',')))
|
||||||
|
dates = sorted([
|
||||||
|
line.order_id.date_order
|
||||||
|
for line in record.sale_order_line_ids
|
||||||
|
if line.order_id.date_order.month in active_months
|
||||||
|
])
|
||||||
|
else:
|
||||||
|
# Cycle habituel (non saisonnier)
|
||||||
|
dates = sorted(record.sale_order_line_ids.mapped('order_id.date_order'))
|
||||||
|
|
||||||
itch_cycle_product_ids = fields.One2many('itch_cycle_product_partner', 'partner_id', string="Itch Cycles Produits")
|
if len(dates) > 1:
|
||||||
itch_next_delay = fields.Date(string="Prochaine Date de Suivi (Itch-Cycle Min)", compute="_compute_itch_next_delay", store=True)
|
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
|
||||||
|
else:
|
||||||
|
record.average_cycle = 0
|
||||||
|
|
||||||
@api.depends('itch_cycle_product_ids.next_follow_up_date')
|
@api.depends('average_cycle')
|
||||||
def _compute_itch_next_delay(self):
|
def _compute_next_expected_date(self):
|
||||||
for partner in self:
|
for record in self:
|
||||||
follow_up_dates = partner.itch_cycle_product_ids.mapped('next_follow_up_date')
|
if record.average_cycle and record.sale_order_line_ids:
|
||||||
partner.itch_next_delay = min(follow_up_dates) if follow_up_dates else False
|
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)
|
||||||
|
else:
|
||||||
|
record.next_expected_date = None
|
||||||
|
|
||||||
|
def _compute_prediction_status(self):
|
||||||
|
today = fields.Date.context_today(self)
|
||||||
|
for record in self:
|
||||||
|
if record.next_expected_date and record.next_expected_date < today:
|
||||||
|
record.prediction_status = 'delayed'
|
||||||
|
else:
|
||||||
|
record.prediction_status = 'on_time'
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def populate_from_past_orders(self):
|
||||||
|
"""
|
||||||
|
Méthode pour traiter le passé :
|
||||||
|
Crée des enregistrements 'itch.cycle.product.partner'
|
||||||
|
basés sur les commandes de vente existantes.
|
||||||
|
"""
|
||||||
|
# Récupérer toutes les lignes de commandes confirmées
|
||||||
|
sale_order_lines = self.env['sale.order.line'].search([
|
||||||
|
('order_id.state', 'in', ['sale', 'done']) # Commandes confirmées ou terminées
|
||||||
|
])
|
||||||
|
|
||||||
|
# Carte pour regrouper par couple (client, produit)
|
||||||
|
cycle_data = {}
|
||||||
|
|
||||||
|
for line in sale_order_lines:
|
||||||
|
key = (line.order_id.partner_id.id, line.product_id.id)
|
||||||
|
if key not in cycle_data:
|
||||||
|
cycle_data[key] = {
|
||||||
|
'partner_id': line.order_id.partner_id.id,
|
||||||
|
'product_id': line.product_id.id,
|
||||||
|
'sale_order_line_ids': []
|
||||||
|
}
|
||||||
|
cycle_data[key]['sale_order_line_ids'].append(line.id) # Ajouter l'ID de la ligne
|
||||||
|
|
||||||
|
# Créer les enregistrements manquants dans Itch Cycle
|
||||||
|
for key, data in cycle_data.items():
|
||||||
|
partner_id, product_id = key
|
||||||
|
|
||||||
|
# Vérifier si l'enregistrement existe déjà
|
||||||
|
itch_cycle = self.search([('partner_id', '=', partner_id), ('product_id', '=', product_id)], limit=1)
|
||||||
|
|
||||||
|
if not itch_cycle:
|
||||||
|
# Calculer le cycle moyen basé sur les dates de commandes
|
||||||
|
order_dates = sorted([self.env['sale.order.line'].browse(line_id).order_id.date_order
|
||||||
|
for line_id in data['sale_order_line_ids']])
|
||||||
|
if len(order_dates) > 1:
|
||||||
|
intervals = [(order_dates[i + 1] - order_dates[i]).days for i in range(len(order_dates) - 1)]
|
||||||
|
average_cycle = sum(intervals) // len(intervals) if intervals else 0
|
||||||
|
else:
|
||||||
|
average_cycle = 0
|
||||||
|
|
||||||
|
# Créer l'enregistrement dans la table Itch Cycle
|
||||||
|
if product_id and partner_id:
|
||||||
|
self.create({
|
||||||
|
'partner_id': partner_id,
|
||||||
|
'product_id': product_id,
|
||||||
|
'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é.")
|
||||||
|
print('valeur de product_id', product_id)
|
||||||
|
print('valeur de partner_id', partner_id)
|
||||||
|
else:
|
||||||
|
# Mettre à jour les lignes de commande si nécessaire
|
||||||
|
itch_cycle.write({
|
||||||
|
'sale_order_line_ids': [(6, 0, data['sale_order_line_ids'])], # Lier ou actualiser les lignes
|
||||||
|
})
|
||||||
|
return True
|
||||||
|
|
|
||||||
15
customer_itch_cycle/models/product_category.py
Normal file
15
customer_itch_cycle/models/product_category.py
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
|
||||||
|
from odoo import models, fields
|
||||||
|
|
||||||
|
class ProductCategory(models.Model):
|
||||||
|
_inherit = 'product.category'
|
||||||
|
|
||||||
|
seasonal_factor = fields.Boolean(
|
||||||
|
string="Catégorie saisonnière",
|
||||||
|
default=False
|
||||||
|
)
|
||||||
|
|
||||||
|
season_months = fields.Char(
|
||||||
|
string="Mois actifs",
|
||||||
|
help="Mois pendant lesquels cette catégorie est active (ex: '3,4,5' pour mars-mai)"
|
||||||
|
)
|
||||||
33
customer_itch_cycle/models/res_partner.py
Normal file
33
customer_itch_cycle/models/res_partner.py
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
from odoo import models, fields, api, _
|
||||||
|
from odoo.exceptions import UserError
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
class ResPartner(models.Model):
|
||||||
|
_inherit = 'res.partner'
|
||||||
|
|
||||||
|
itch_cycle_product_ids = fields.One2many(
|
||||||
|
comodel_name='itch.cycle.product.partner',
|
||||||
|
inverse_name='partner_id',
|
||||||
|
string="Itch Cycles Produits"
|
||||||
|
)
|
||||||
|
|
||||||
|
itch_next_delay = fields.Date(
|
||||||
|
string="Prochaine Date de Suivi (Itch-Cycle Min)",
|
||||||
|
compute="_compute_itch_next_delay",
|
||||||
|
store=True
|
||||||
|
)
|
||||||
|
|
||||||
|
@api.depends('itch_cycle_product_ids.next_follow_up_date')
|
||||||
|
def _compute_itch_next_delay(self):
|
||||||
|
"""
|
||||||
|
Calcule la date de suivi minimale parmi tous les cycles de produits associés.
|
||||||
|
"""
|
||||||
|
for partner in self:
|
||||||
|
follow_up_dates = partner.itch_cycle_product_ids.mapped('next_follow_up_date')
|
||||||
|
partner.itch_next_delay = min(follow_up_dates) if follow_up_dates else False
|
||||||
|
|
||||||
|
def action_populate_itch_cycles(self):
|
||||||
|
"""
|
||||||
|
Action bouton qui appelle la méthode pour peupler les cycles depuis les données passées.
|
||||||
|
"""
|
||||||
|
self.env['itch.cycle.product.partner'].populate_from_past_orders()
|
||||||
|
|
@ -1,44 +0,0 @@
|
||||||
|
|
||||||
from odoo import models, fields, api, _
|
|
||||||
from odoo.exceptions import UserError
|
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
class SaleOrder(models.Model):
|
|
||||||
_inherit = 'sale.order'
|
|
||||||
|
|
||||||
def action_confirm(self):
|
|
||||||
super(SaleOrder, self).action_confirm()
|
|
||||||
for line in self.order_line:
|
|
||||||
partner_id = self.partner_id
|
|
||||||
product_id = line.product_id
|
|
||||||
|
|
||||||
itch_cycle_record = self.env['itch_cycle_product_partner'].search([
|
|
||||||
('partner_id', '=', partner_id.id),
|
|
||||||
('product_id', '=', product_id.id)
|
|
||||||
], limit=1)
|
|
||||||
|
|
||||||
if itch_cycle_record:
|
|
||||||
if itch_cycle_record.last_purchase_date:
|
|
||||||
days_since_last_purchase = (datetime.now().date() - itch_cycle_record.last_purchase_date).days
|
|
||||||
if itch_cycle_record.itch_cycle_duration == 0:
|
|
||||||
raise UserError(_(
|
|
||||||
f"Veuillez définir la durée du itch-cycle pour le produit '{product_id.name}' "
|
|
||||||
f"pour le client '{partner_id.name}'.\n\n"
|
|
||||||
f"Il y a eu {days_since_last_purchase} jours depuis le dernier achat."
|
|
||||||
))
|
|
||||||
else:
|
|
||||||
itch_cycle_record.last_purchase_date = datetime.now().date()
|
|
||||||
itch_cycle_record.itch_cycle_duration = days_since_last_purchase
|
|
||||||
else:
|
|
||||||
itch_cycle_record.last_purchase_date = datetime.now().date()
|
|
||||||
else:
|
|
||||||
new_record = self.env['itch_cycle_product_partner'].create({
|
|
||||||
'partner_id': partner_id.id,
|
|
||||||
'product_id': product_id.id,
|
|
||||||
'last_purchase_date': datetime.now().date(),
|
|
||||||
'itch_cycle_duration': 0
|
|
||||||
})
|
|
||||||
raise UserError(_(
|
|
||||||
f"Il n'y a pas encore de itch-cycle défini pour le produit '{product_id.name}' "
|
|
||||||
f"avec le client '{partner_id.name}'.\n\nVeuillez entrer une durée pour ce cycle."
|
|
||||||
))
|
|
||||||
27
customer_itch_cycle/models/sale_order_line.py
Normal file
27
customer_itch_cycle/models/sale_order_line.py
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
from odoo import models, fields, api
|
||||||
|
|
||||||
|
class SaleOrderLine(models.Model):
|
||||||
|
_inherit = 'sale.order.line'
|
||||||
|
|
||||||
|
itch_cycle_id = fields.Many2one(
|
||||||
|
comodel_name='itch.cycle.product.partner',
|
||||||
|
string="Cycle Produit/Partenaire"
|
||||||
|
)
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def create(self, vals):
|
||||||
|
line = super(SaleOrderLine, self).create(vals)
|
||||||
|
# Associer au bon cycle produit/partenaire
|
||||||
|
if vals.get('product_id') and vals.get('order_id'):
|
||||||
|
partner_id = self.env['sale.order'].browse(vals['order_id']).partner_id.id
|
||||||
|
itch_cycle = self.env['itch.cycle.product.partner'].search([
|
||||||
|
('partner_id', '=', partner_id),
|
||||||
|
('product_id', '=', vals['product_id'])
|
||||||
|
], limit=1)
|
||||||
|
if not itch_cycle:
|
||||||
|
itch_cycle = self.env['itch.cycle.product.partner'].create({
|
||||||
|
'partner_id': partner_id,
|
||||||
|
'product_id': vals['product_id'],
|
||||||
|
})
|
||||||
|
line.itch_cycle_id = itch_cycle.id
|
||||||
|
return line
|
||||||
2
customer_itch_cycle/security/ir.model.access.csv
Normal file
2
customer_itch_cycle/security/ir.model.access.csv
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||||
|
access_itch_cycle_product_partner,itch.cycle.product.partner access,model_itch_cycle_product_partner,base.group_user,1,1,1,1
|
||||||
|
0
customer_itch_cycle/views/itch_cycle_menu.xml
Normal file
0
customer_itch_cycle/views/itch_cycle_menu.xml
Normal file
|
|
@ -1,44 +1,71 @@
|
||||||
|
|
||||||
<odoo>
|
<odoo>
|
||||||
<record id="view_itch_cycle_product_partner_tree" model="ir.ui.view">
|
<record id="view_itch_cycle_product_partner_tree" model="ir.ui.view">
|
||||||
<field name="name">itch.cycle.product.partner.tree</field>
|
<field name="name">itch.cycle.product.partner.tree</field>
|
||||||
<field name="model">itch_cycle_product_partner</field>
|
<field name="model">itch.cycle.product.partner</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<tree>
|
<tree>
|
||||||
<field name="partner_id"/>
|
<field name="partner_id"/>
|
||||||
<field name="product_id"/>
|
<field name="product_id"/>
|
||||||
<field name="last_purchase_date"/>
|
|
||||||
<field name="itch_cycle_duration"/>
|
<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="next_follow_up_date"/>
|
||||||
|
<field name="prediction_status"/>
|
||||||
</tree>
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="view_itch_cycle_product_partner_form" model="ir.ui.view">
|
<record id="view_itch_cycle_product_partner_form" model="ir.ui.view">
|
||||||
<field name="name">itch.cycle.product.partner.form</field>
|
<field name="name">itch.cycle.product.partner.form</field>
|
||||||
<field name="model">itch_cycle_product_partner</field>
|
<field name="model">itch.cycle.product.partner</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<form>
|
<form>
|
||||||
|
<header>
|
||||||
|
<field name="prediction_status" widget="statusbar" options="{'on_time': 'green', 'delayed': 'red'}"/>
|
||||||
|
</header>
|
||||||
<sheet>
|
<sheet>
|
||||||
<group>
|
<group>
|
||||||
|
<group string="Informations principales">
|
||||||
<field name="partner_id"/>
|
<field name="partner_id"/>
|
||||||
<field name="product_id"/>
|
<field name="product_id"/>
|
||||||
<field name="last_purchase_date"/>
|
</group>
|
||||||
<field name="itch_cycle_duration"/>
|
<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="next_follow_up_date"/>
|
||||||
</group>
|
</group>
|
||||||
|
</group>
|
||||||
|
<notebook>
|
||||||
|
<page string="Lignes de commandes associées">
|
||||||
|
<field name="sale_order_line_ids">
|
||||||
|
<tree editable="bottom">
|
||||||
|
<field name="order_id"/>
|
||||||
|
<field name="product_id"/>
|
||||||
|
<field name="price_unit"/>
|
||||||
|
<field name="product_uom_qty"/>
|
||||||
|
<field name="discount"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</page>
|
||||||
|
</notebook>
|
||||||
</sheet>
|
</sheet>
|
||||||
</form>
|
</form>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<menuitem id="menu_itch_cycle_product_partner" name="Itch Cycles"
|
|
||||||
parent="sale.sale_order_menu"
|
|
||||||
action="action_itch_cycle_product_partner"/>
|
|
||||||
|
|
||||||
<record id="action_itch_cycle_product_partner" model="ir.actions.act_window">
|
<record id="action_itch_cycle_product_partner" model="ir.actions.act_window">
|
||||||
<field name="name">Itch Cycles</field>
|
<field name="name">Itch Cycles Produits</field>
|
||||||
<field name="res_model">itch_cycle_product_partner</field>
|
<field name="res_model">itch.cycle.product.partner</field>
|
||||||
<field name="view_mode">tree,form</field>
|
<field name="view_mode">tree,form</field>
|
||||||
|
<field name="view_id" ref="view_itch_cycle_product_partner_tree"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
<menuitem id="menu_itch_cycle_product_root"
|
||||||
|
name="Itch Cycle"
|
||||||
|
sequence="10"
|
||||||
|
parent="sale.menu_sale_report"
|
||||||
|
action="action_itch_cycle_product_partner"/>
|
||||||
</odoo>
|
</odoo>
|
||||||
21
customer_itch_cycle/views/product_category_view.xml
Normal file
21
customer_itch_cycle/views/product_category_view.xml
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
|
||||||
|
<odoo>
|
||||||
|
<record id="view_product_category_form" model="ir.ui.view">
|
||||||
|
<field name="name">product.category.form</field>
|
||||||
|
<field name="model">product.category</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form string="Catégorie de produit">
|
||||||
|
<sheet>
|
||||||
|
<group>
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="parent_id"/>
|
||||||
|
</group>
|
||||||
|
<group string="Saisonnalité">
|
||||||
|
<field name="seasonal_factor"/>
|
||||||
|
<field name="season_months"/>
|
||||||
|
</group>
|
||||||
|
</sheet>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
|
|
@ -5,7 +5,12 @@
|
||||||
<field name="model">res.partner</field>
|
<field name="model">res.partner</field>
|
||||||
<field name="inherit_id" ref="base.view_partner_form"/>
|
<field name="inherit_id" ref="base.view_partner_form"/>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<sheet position="before">
|
<notebook position="inside">
|
||||||
|
<page string="Itch Cycle" name="itch_cycle">
|
||||||
|
<button name="action_populate_itch_cycles"
|
||||||
|
string="Traiter l'historique des cycles"
|
||||||
|
type="object"
|
||||||
|
class="btn-primary"/>
|
||||||
<group string="Itch Cycle Information">
|
<group string="Itch Cycle Information">
|
||||||
<field name="itch_next_delay" readonly="1"/>
|
<field name="itch_next_delay" readonly="1"/>
|
||||||
<field name="itch_cycle_product_ids" readonly="1">
|
<field name="itch_cycle_product_ids" readonly="1">
|
||||||
|
|
@ -17,7 +22,8 @@
|
||||||
</tree>
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
</group>
|
</group>
|
||||||
</sheet>
|
</page>
|
||||||
|
</notebook>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,8 @@ class PartnerPurchaseAnalysisWizard(models.TransientModel):
|
||||||
if self.date_end:
|
if self.date_end:
|
||||||
domain.append(('order_id.date_order', '<=', self.date_end))
|
domain.append(('order_id.date_order', '<=', self.date_end))
|
||||||
|
|
||||||
|
domain.append(('order_id.state', 'in', ['sale', 'done']))
|
||||||
|
|
||||||
sale_order_lines = self.env['sale.order.line'].search(domain)
|
sale_order_lines = self.env['sale.order.line'].search(domain)
|
||||||
|
|
||||||
if not sale_order_lines:
|
if not sale_order_lines:
|
||||||
|
|
@ -82,10 +84,14 @@ class PartnerPurchaseAnalysisWizard(models.TransientModel):
|
||||||
user_lang_name = self.env['res.lang'].search([('code', '=', user_lang)], limit=1).name or "English"
|
user_lang_name = self.env['res.lang'].search([('code', '=', user_lang)], limit=1).name or "English"
|
||||||
|
|
||||||
prompt = (
|
prompt = (
|
||||||
"Analyze the following customer purchase history. Identify trends, product "
|
"Analyze all the sale order lines of the following customer. Identify trends, "
|
||||||
"category preferences, and any significant deviations. Respond in "
|
"and any significant deviations. Respond in "
|
||||||
f"{user_lang_name}. Produce graph and table of the analysis. You output all "
|
f"{user_lang_name}. Produce the analysis adding next plan purchase or lost purchase. You output all "
|
||||||
"in html format."
|
"in html format. Focus on missing product order and deviation from the average. "
|
||||||
|
"Be sure to list all products and categories in the analysis. Try to identify "
|
||||||
|
"the next purchase date for all product and warn if the customer is not buying "
|
||||||
|
"and identify recurring sale and product not sale. Put in the analysis if the product is not bought "
|
||||||
|
"in the last 12 months and show those product as lost sale with a value of the lost sales"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Construction de `purchase_details` pour le contenu du prompt
|
# Construction de `purchase_details` pour le contenu du prompt
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue