itch work?
This commit is contained in:
parent
d644841f99
commit
010752b01f
22 changed files with 1517 additions and 338 deletions
|
|
@ -1,2 +1,2 @@
|
|||
|
||||
from . import models
|
||||
from . import wizards
|
||||
|
|
@ -1,24 +1,37 @@
|
|||
{
|
||||
'name': 'Customer Itch Cycle Management',
|
||||
'version': '1.0',
|
||||
'name': 'Customer Itch Cycle',
|
||||
'version': '17.0.1.0.0',
|
||||
'category': 'Sales',
|
||||
'summary': 'Manage customer product cycles and predict future sales',
|
||||
'description': """
|
||||
This module helps track and manage customer purchasing cycles:
|
||||
* Track product purchase cycles per customer
|
||||
* Predict next purchase dates
|
||||
* Generate opportunities based on predictions
|
||||
* Monitor delayed purchases
|
||||
""",
|
||||
'author': 'DurPro',
|
||||
'website': 'https://www.durpro.com',
|
||||
'depends': [
|
||||
'base',
|
||||
'sale_stock',
|
||||
'web'
|
||||
'sale',
|
||||
'sale_management',
|
||||
'crm',
|
||||
'sale_crm',
|
||||
'base_automation',
|
||||
],
|
||||
'author': 'Benoit Vézina',
|
||||
'category': 'Sales Management',
|
||||
'summary': 'Manage customer itch cycles by product for proactive sales engagement.',
|
||||
'website': 'https://www.bemade.org',
|
||||
'description': "Manage customer itch cycles by product for proactive sales engagement.",
|
||||
'license': 'AGPL-3',
|
||||
'data': [
|
||||
'security/ir.model.access.csv',
|
||||
'data/month_data.xml',
|
||||
'wizards/apply_to_child_categories.xml',
|
||||
'views/itch_cycle_product_partner_view.xml',
|
||||
'views/res_partner_view.xml',
|
||||
'views/product_category_view.xml',
|
||||
'views/itch_cycle_actions.xml',
|
||||
'views/itch_cycle_menu.xml',
|
||||
'security/ir.model.access.csv'
|
||||
'views/crm_lead_view.xml',
|
||||
'views/product_category_view.xml',
|
||||
'data/ir_cron_data.xml',
|
||||
# 'data/crm_automation_data.xml',
|
||||
],
|
||||
'assets': {
|
||||
'web.assets_backend': [
|
||||
|
|
@ -27,5 +40,6 @@
|
|||
],
|
||||
},
|
||||
'installable': True,
|
||||
'application': False,
|
||||
'application': True,
|
||||
'license': 'LGPL-3',
|
||||
}
|
||||
|
|
|
|||
13
customer_itch_cycle/data/ir_actions_server.xml
Normal file
13
customer_itch_cycle/data/ir_actions_server.xml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="action_reprocess_all_itch_cycles" model="ir.actions.server">
|
||||
<field name="name">Reprocess All Cycles</field>
|
||||
<field name="model_id" ref="model_itch_cycle_product_partner"/>
|
||||
<field name="binding_model_id" ref="model_itch_cycle_product_partner"/>
|
||||
<field name="binding_view_types">list,form</field>
|
||||
<field name="state">code</field>
|
||||
<field name="code">
|
||||
env['itch.cycle.product.partner'].populate_from_past_orders()
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
34
customer_itch_cycle/data/ir_cron_data.xml
Normal file
34
customer_itch_cycle/data/ir_cron_data.xml
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<!-- Automated Task for Creating Opportunities -->
|
||||
<record id="ir_cron_create_opportunities" model="ir.cron">
|
||||
<field name="name">Sales Cycles: Create Opportunities</field>
|
||||
<field name="model_id" ref="model_itch_cycle_product_partner"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">model._cron_create_opportunities()</field>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="interval_number">1</field>
|
||||
<field name="interval_type">days</field>
|
||||
<field name="numbercall">-1</field>
|
||||
<field name="doall" eval="False"/>
|
||||
<field name="priority">5</field>
|
||||
<field name="active" eval="True"/>
|
||||
</record>
|
||||
|
||||
<!-- Automated Task for Processing History -->
|
||||
<record id="ir_cron_process_history" model="ir.cron">
|
||||
<field name="name">Sales Cycles: Process History</field>
|
||||
<field name="model_id" ref="model_itch_cycle_product_partner"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">model.populate_from_past_orders()</field>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="interval_number">1</field>
|
||||
<field name="interval_type">days</field>
|
||||
<field name="numbercall">-1</field>
|
||||
<field name="doall" eval="False"/>
|
||||
<field name="priority">10</field>
|
||||
<field name="active" eval="True"/>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
66
customer_itch_cycle/data/month_data.xml
Normal file
66
customer_itch_cycle/data/month_data.xml
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<!-- Initialize months -->
|
||||
<record id="month_01" model="itch.month">
|
||||
<field name="name">January</field>
|
||||
<field name="sequence">1</field>
|
||||
<field name="number">1</field>
|
||||
</record>
|
||||
<record id="month_02" model="itch.month">
|
||||
<field name="name">February</field>
|
||||
<field name="sequence">2</field>
|
||||
<field name="number">2</field>
|
||||
</record>
|
||||
<record id="month_03" model="itch.month">
|
||||
<field name="name">March</field>
|
||||
<field name="sequence">3</field>
|
||||
<field name="number">3</field>
|
||||
</record>
|
||||
<record id="month_04" model="itch.month">
|
||||
<field name="name">April</field>
|
||||
<field name="sequence">4</field>
|
||||
<field name="number">4</field>
|
||||
</record>
|
||||
<record id="month_05" model="itch.month">
|
||||
<field name="name">May</field>
|
||||
<field name="sequence">5</field>
|
||||
<field name="number">5</field>
|
||||
</record>
|
||||
<record id="month_06" model="itch.month">
|
||||
<field name="name">June</field>
|
||||
<field name="sequence">6</field>
|
||||
<field name="number">6</field>
|
||||
</record>
|
||||
<record id="month_07" model="itch.month">
|
||||
<field name="name">July</field>
|
||||
<field name="sequence">7</field>
|
||||
<field name="number">7</field>
|
||||
</record>
|
||||
<record id="month_08" model="itch.month">
|
||||
<field name="name">August</field>
|
||||
<field name="sequence">8</field>
|
||||
<field name="number">8</field>
|
||||
</record>
|
||||
<record id="month_09" model="itch.month">
|
||||
<field name="name">September</field>
|
||||
<field name="sequence">9</field>
|
||||
<field name="number">9</field>
|
||||
</record>
|
||||
<record id="month_10" model="itch.month">
|
||||
<field name="name">October</field>
|
||||
<field name="sequence">10</field>
|
||||
<field name="number">10</field>
|
||||
</record>
|
||||
<record id="month_11" model="itch.month">
|
||||
<field name="name">November</field>
|
||||
<field name="sequence">11</field>
|
||||
<field name="number">11</field>
|
||||
</record>
|
||||
<record id="month_12" model="itch.month">
|
||||
<field name="name">December</field>
|
||||
<field name="sequence">12</field>
|
||||
<field name="number">12</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
|
|
@ -2,3 +2,4 @@ from . import itch_cycle_product_partner
|
|||
from . import sale_order_line
|
||||
from . import product_category
|
||||
from . import res_partner
|
||||
from . import crm_lead
|
||||
|
|
|
|||
42
customer_itch_cycle/models/crm_lead.py
Normal file
42
customer_itch_cycle/models/crm_lead.py
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
from odoo import models, fields
|
||||
|
||||
|
||||
class CrmLead(models.Model):
|
||||
"""
|
||||
Extend CRM Lead model to add purchase cycle information.
|
||||
|
||||
Adds a link to the purchase cycle associated with the opportunity.
|
||||
This allows tracking customer purchasing patterns and forecasting
|
||||
future opportunities based on historical data.
|
||||
"""
|
||||
|
||||
_inherit = 'crm.lead'
|
||||
|
||||
itch_cycle_id = fields.Many2one(
|
||||
comodel_name='itch.cycle.product.partner',
|
||||
string='Purchase Cycle',
|
||||
ondelete='set null',
|
||||
help="""
|
||||
Linked purchase cycle for this opportunity.
|
||||
Used to track the customer's purchasing patterns.
|
||||
"""
|
||||
)
|
||||
|
||||
date_last_purchase = fields.Date(
|
||||
related='itch_cycle_id.date_last_purchase',
|
||||
string='Last Purchase Date',
|
||||
readonly=True
|
||||
)
|
||||
|
||||
cycle_duration = fields.Integer(
|
||||
related='itch_cycle_id.cycle_duration',
|
||||
string='Cycle Duration (days)',
|
||||
readonly=True
|
||||
)
|
||||
|
||||
cycle_sales = fields.One2many(
|
||||
comodel_name='sale.order.line',
|
||||
related='itch_cycle_id.sale_order_line_ids',
|
||||
string='Cycle Sales',
|
||||
readonly=True
|
||||
)
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,15 +1,124 @@
|
|||
from odoo import models, fields, api
|
||||
from odoo.exceptions import ValidationError
|
||||
import logging
|
||||
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
class Month(models.Model):
|
||||
"""
|
||||
Model to represent months for seasonal tracking.
|
||||
This allows for better organization and selection of active months
|
||||
for seasonal product categories.
|
||||
"""
|
||||
_name = 'itch.month'
|
||||
_description = 'Month for Seasonal Tracking'
|
||||
_order = 'sequence'
|
||||
|
||||
name = fields.Char(string='Name', required=True, translate=True)
|
||||
sequence = fields.Integer(string='Sequence', required=True)
|
||||
number = fields.Integer(string='Month Number', required=True)
|
||||
|
||||
_sql_constraints = [
|
||||
('unique_number', 'unique(number)', 'Month number must be unique!'),
|
||||
('number_range', 'CHECK(number >= 1 AND number <= 12)', 'Month number must be between 1 and 12!')
|
||||
]
|
||||
|
||||
from odoo import models, fields
|
||||
|
||||
class ProductCategory(models.Model):
|
||||
"""
|
||||
Extends the product category model to add seasonal behavior functionality.
|
||||
|
||||
This is used in the sales cycle calculation to better predict next sales
|
||||
by taking into account seasonal patterns.
|
||||
"""
|
||||
_inherit = 'product.category'
|
||||
|
||||
seasonal_factor = fields.Boolean(
|
||||
string="Catégorie saisonnière",
|
||||
default=False
|
||||
is_cycle_tracked = fields.Boolean(
|
||||
string="Track Sales Cycle",
|
||||
help="If enabled, products in this category will be tracked by the sales cycle system",
|
||||
default=True,
|
||||
)
|
||||
|
||||
season_months = fields.Char(
|
||||
string="Mois actifs",
|
||||
help="Mois pendant lesquels cette catégorie est active (ex: '3,4,5' pour mars-mai)"
|
||||
temp_is_cycle_tracked = fields.Boolean(
|
||||
string="Temporary Track Sales Cycle",
|
||||
help="Technical field to track changes in is_cycle_tracked",
|
||||
default=True,
|
||||
)
|
||||
|
||||
@api.onchange('is_cycle_tracked')
|
||||
def _onchange_is_cycle_tracked(self):
|
||||
"""Store the new value temporarily and restore the original value"""
|
||||
if self.id and self.is_cycle_tracked != self.temp_is_cycle_tracked:
|
||||
self.temp_is_cycle_tracked = self.is_cycle_tracked
|
||||
self.is_cycle_tracked = not self.is_cycle_tracked
|
||||
return {
|
||||
'warning': {
|
||||
'title': 'Confirmation Required',
|
||||
'message': 'Please use the "Apply Changes" button to change tracking status.'
|
||||
}
|
||||
}
|
||||
|
||||
def action_apply_cycle_tracked(self):
|
||||
"""Open wizard to apply cycle tracked changes"""
|
||||
self.ensure_one()
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': 'Apply to Child Categories',
|
||||
'res_model': 'itch.apply.to.child.categories',
|
||||
'view_mode': 'form',
|
||||
'target': 'new',
|
||||
'context': {
|
||||
'default_category_id': self.id,
|
||||
'default_new_value': self.temp_is_cycle_tracked,
|
||||
}
|
||||
}
|
||||
|
||||
def write(self, vals):
|
||||
"""
|
||||
Override write to handle changes in is_cycle_tracked field.
|
||||
|
||||
If a category is no longer tracked, we archive all related cycles.
|
||||
"""
|
||||
if 'is_cycle_tracked' in vals and not vals['is_cycle_tracked']:
|
||||
# Find all cycles for products in this category
|
||||
cycles = self.env['itch.cycle.product.partner'].search([
|
||||
('product_id.categ_id', 'in', self.ids)
|
||||
])
|
||||
if cycles:
|
||||
# Archive the cycles
|
||||
cycles.write({
|
||||
'active': False,
|
||||
'notes': f'{fields.Date.today()}: Archived automatically - Category no longer tracked'
|
||||
})
|
||||
# Log the action
|
||||
_logger.info(
|
||||
f"Archived {len(cycles)} cycles due to category {self.name} "
|
||||
f"being marked as not tracked"
|
||||
)
|
||||
|
||||
return super().write(vals)
|
||||
|
||||
seasonal_factor = fields.Boolean(
|
||||
string="Seasonal Category",
|
||||
help="Enable this if the products in this category have seasonal sales patterns",
|
||||
default=False,
|
||||
)
|
||||
|
||||
season_months = fields.Many2many(
|
||||
'itch.month',
|
||||
string="Active Months",
|
||||
help="Select the months when this category is typically active",
|
||||
)
|
||||
|
||||
@api.constrains('season_months')
|
||||
def _check_season_months(self):
|
||||
"""
|
||||
Validate the selection of season_months field.
|
||||
|
||||
Rules:
|
||||
- Cannot be empty if seasonal_factor is True
|
||||
"""
|
||||
for record in self:
|
||||
if record.seasonal_factor and not record.season_months:
|
||||
raise ValidationError("Active months must be specified for seasonal categories")
|
||||
|
|
|
|||
|
|
@ -1,43 +1,53 @@
|
|||
from odoo import models, fields, api, _
|
||||
from odoo.exceptions import UserError
|
||||
from datetime import datetime
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class ResPartner(models.Model):
|
||||
"""
|
||||
Extends the partner model to add sales cycle tracking functionality.
|
||||
"""
|
||||
_inherit = 'res.partner'
|
||||
|
||||
itch_cycle_product_ids = fields.One2many(
|
||||
comodel_name='itch.cycle.product.partner',
|
||||
inverse_name='partner_id',
|
||||
string="Itch Cycles Products"
|
||||
string="Product Sales Cycles",
|
||||
help="List of all product sales cycles associated with this customer",
|
||||
)
|
||||
|
||||
reorder_cycle_product_ids = fields.One2many(
|
||||
comodel_name='itch.cycle.product.partner',
|
||||
inverse_name='partner_id',
|
||||
string="Reorder Cycles Products",
|
||||
domain=[('quantity_of_orders', '>', 1)]
|
||||
string="Active Sales Cycles",
|
||||
domain=[('quantity_of_orders', '>', 1)],
|
||||
help="List of product sales cycles with established patterns",
|
||||
)
|
||||
|
||||
itch_next_delay = fields.Date(
|
||||
string="Prochaine Date de Suivi (Itch-Cycle Min)",
|
||||
# compute="_compute_itch_next_delay",
|
||||
store=True
|
||||
string="Next Follow-up Date",
|
||||
compute="_compute_itch_next_delay",
|
||||
store=True,
|
||||
help="Earliest follow-up date",
|
||||
)
|
||||
|
||||
@api.depends('itch_cycle_product_ids.next_follow_up_date')
|
||||
@api.depends(
|
||||
'itch_cycle_product_ids',
|
||||
'itch_cycle_product_ids.date_next_follow_up'
|
||||
)
|
||||
def _compute_itch_next_delay(self):
|
||||
"""
|
||||
Calcule la date de suivi minimale parmi tous les cycles de produits associés.
|
||||
"""
|
||||
Compute the earliest follow-up date.
|
||||
|
||||
The date is calculated from all associated product cycles.
|
||||
"""
|
||||
for partner in self:
|
||||
follow_up_dates = partner.reorder_cycle_product_ids.mapped('next_follow_up_date')
|
||||
if follow_up_dates:
|
||||
partner.itch_next_delay = min(follow_up_dates)
|
||||
else:
|
||||
partner.itch_next_delay = False
|
||||
cycles = partner.reorder_cycle_product_ids
|
||||
dates = cycles.mapped('date_next_follow_up')
|
||||
partner.itch_next_delay = min(dates) if 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.
|
||||
Initialize or update product cycles.
|
||||
|
||||
The cycles are created from historical order data.
|
||||
"""
|
||||
self.env['itch.cycle.product.partner'].populate_from_past_orders()
|
||||
self.env['itch.cycle.product.partner'].populate_from_past_orders()
|
||||
|
|
|
|||
|
|
@ -1,43 +1,51 @@
|
|||
from odoo import models, fields, api
|
||||
|
||||
|
||||
class SaleOrderLine(models.Model):
|
||||
"""
|
||||
Extends sale order line to add itch cycle tracking.
|
||||
"""
|
||||
_inherit = 'sale.order.line'
|
||||
|
||||
itch_cycle_id = fields.Many2one(
|
||||
comodel_name='itch.cycle.product.partner',
|
||||
string="Cycle Produit/Partenaire"
|
||||
)
|
||||
string="Cycle")
|
||||
|
||||
stock_move_ids = fields.One2many(
|
||||
comodel_name='stock.move',
|
||||
inverse_name='sale_line_id',
|
||||
string="Mouvements de stock"
|
||||
)
|
||||
string="Movements")
|
||||
|
||||
sale_date = fields.Datetime(
|
||||
string="Date de vente",
|
||||
string="Date",
|
||||
related='order_id.date_order',
|
||||
store=True
|
||||
)
|
||||
store=True)
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
# Appeler le super pour créer les lignes
|
||||
"""
|
||||
Create sale order lines and associate them with their itch cycle.
|
||||
|
||||
If the product is cycle tracked and no existing cycle exists for the
|
||||
product/partner combination, a new cycle is created.
|
||||
"""
|
||||
lines = super().create(vals_list)
|
||||
|
||||
# Associer les lignes nouvellement créées avec leur cycle
|
||||
for line in lines:
|
||||
if line.product_id and line.order_id:
|
||||
if (line.product_id and line.order_id and
|
||||
line.product_id.categ_id.is_cycle_tracked):
|
||||
partner_id = line.order_id.partner_id.id
|
||||
itch_cycle = self.env['itch.cycle.product.partner'].search([
|
||||
('partner_id', '=', partner_id),
|
||||
('product_id', '=', line.product_id.id)
|
||||
], limit=1)
|
||||
if not itch_cycle:
|
||||
itch_cycle = self.env['itch.cycle.product.partner'].create({
|
||||
cycle_vals = {
|
||||
'partner_id': partner_id,
|
||||
'product_id': line.product_id.id,
|
||||
})
|
||||
}
|
||||
itch_cycle = self.env['itch.cycle.product.partner'].create(
|
||||
cycle_vals)
|
||||
line.itch_cycle_id = itch_cycle.id
|
||||
|
||||
return lines
|
||||
return lines
|
||||
|
|
|
|||
|
|
@ -1,2 +1,5 @@
|
|||
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
|
||||
access_itch_cycle_product_partner,itch.cycle.product.partner access,model_itch_cycle_product_partner,base.group_user,1,1,1,1
|
||||
access_itch_month_user,itch.month access user,model_itch_month,base.group_user,1,0,0,0
|
||||
access_itch_month_manager,itch.month access manager,model_itch_month,sales_team.group_sale_manager,1,1,1,1
|
||||
access_itch_apply_to_child_categories,itch.apply.to.child.categories access,model_itch_apply_to_child_categories,base.group_user,1,1,1,1
|
||||
|
|
|
@ -5,6 +5,10 @@ import { listView } from "@web/views/list/list_view";
|
|||
import { ListController } from "@web/views/list/list_controller";
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
|
||||
/**
|
||||
* Custom List Controller for Product/Partner Cycle Management
|
||||
* Extends standard list view to add history processing functionality
|
||||
*/
|
||||
class CycleProductPartnerListController extends ListController {
|
||||
setup() {
|
||||
super.setup();
|
||||
|
|
@ -12,22 +16,41 @@ class CycleProductPartnerListController extends ListController {
|
|||
this.notification = useService("notification");
|
||||
}
|
||||
|
||||
/**
|
||||
* Process historical sales data to update cycles
|
||||
* Shows notifications for processing status
|
||||
*/
|
||||
async ProcessHistoryButton() {
|
||||
try {
|
||||
await this.orm.call("itch.cycle.product.partner", "populate_from_past_orders", []);
|
||||
this.notification.add("L'action a été exécutée avec succès.", {
|
||||
type: "success",
|
||||
this.notification.add("Starting history processing...", {
|
||||
type: "info",
|
||||
sticky: false,
|
||||
});
|
||||
|
||||
const result = await this.orm.call("itch.cycle.product.partner", "populate_from_past_orders", []);
|
||||
|
||||
if (result && result.tag === 'display_notifications' && result.params.notifications) {
|
||||
for (const notif of result.params.notifications) {
|
||||
this.notification.add(notif.params.message, {
|
||||
type: notif.params.type,
|
||||
sticky: notif.params.sticky,
|
||||
});
|
||||
await new Promise(resolve => setTimeout(resolve, 300));
|
||||
}
|
||||
}
|
||||
|
||||
await this.model.load();
|
||||
} catch (error) {
|
||||
this.notification.add(`Une erreur s'est produite : ${error.message}`, {
|
||||
this.notification.add(`An error occurred: ${error.message}`, {
|
||||
type: "danger",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Register the custom list view for cycle product partner
|
||||
registry.category("views").add("cycle_product_partner_list", {
|
||||
...listView,
|
||||
Controller: CycleProductPartnerListController,
|
||||
buttonTemplate: "customer_itch_cycle.process_history_button_in_tree",
|
||||
});
|
||||
buttonTemplate: "customer_itch_cycle.ListButtons",
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
<t t-name="customer_itch_cycle.process_history_button_in_tree" name="Button in Tree">
|
||||
<button class="btn btn-primary" t-on-click="ProcessHistoryButton">
|
||||
Mettre à jour depuis commandes passées
|
||||
</button>
|
||||
<t t-name="customer_itch_cycle.ListButtons" name="ProcessHistoryButton">
|
||||
<div>
|
||||
<button class="btn btn-primary" t-on-click="ProcessHistoryButton">
|
||||
Update from Sales History
|
||||
</button>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
51
customer_itch_cycle/views/crm_lead_view.xml
Normal file
51
customer_itch_cycle/views/crm_lead_view.xml
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
<!-- Add itch cycle field to CRM lead form -->
|
||||
<record id="view_crm_lead_form_inherit_itch_cycle" model="ir.ui.view">
|
||||
<field name="name">crm.lead.form.inherit.itch.cycle</field>
|
||||
<field name="model">crm.lead</field>
|
||||
<field name="inherit_id" ref="crm.crm_lead_view_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='partner_id']" position="after">
|
||||
<field name="itch_cycle_id" widget="many2one"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Add itch cycle tab to CRM lead form -->
|
||||
<record id="view_crm_lead_form_inherit_itch_cycle_tab" model="ir.ui.view">
|
||||
<field name="name">crm.lead.form.inherit.itch.cycle.tab</field>
|
||||
<field name="model">crm.lead</field>
|
||||
<field name="inherit_id" ref="crm.crm_lead_view_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//notebook" position="inside">
|
||||
<page string="Purchase Cycle">
|
||||
<field name="itch_cycle_id" readonly="1"/>
|
||||
</page>
|
||||
<page string="Opportunities">
|
||||
<tree>
|
||||
<field name="expected_revenue"/>
|
||||
<field name="probability"/>
|
||||
<field name="date_deadline"/>
|
||||
<field name="stage_id"/>
|
||||
<field name="tag_ids"/>
|
||||
</tree>
|
||||
<field name="description" widget="html"/>
|
||||
</page>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Add itch cycle filter to CRM lead search -->
|
||||
<record id="view_crm_lead_search_inherit_itch_cycle" model="ir.ui.view">
|
||||
<field name="name">crm.lead.search.inherit.itch.cycle</field>
|
||||
<field name="model">crm.lead</field>
|
||||
<field name="inherit_id" ref="crm.view_crm_case_opportunities_filter"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//filter[@name='assigned_to_me']" position="after">
|
||||
<filter string="Purchase Cycle" name="itch_cycle"
|
||||
domain="[('itch_cycle_id','!=',False)]"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
@ -6,6 +6,8 @@
|
|||
<field name="res_model">itch.cycle.product.partner</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="view_id" ref="view_cycle_product_partner_tree"/>
|
||||
<field name="domain">[('state', '!=', 'new'), ('active', '=', True)]
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
|
@ -1,19 +1,72 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
<record id="action_itch_cycle_product_partner" model="ir.actions.act_window">
|
||||
<field name="name">Cycles Produit/Partenaire</field>
|
||||
<field name="res_model">itch.cycle.product.partner</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="display_name">name_get</field>
|
||||
</record>
|
||||
|
||||
<record id="action_create_batch_opportunities" model="ir.actions.server">
|
||||
<field name="name">Create Opportunities</field>
|
||||
<field name="model_id" ref="customer_itch_cycle.model_itch_cycle_product_partner"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">records.action_create_batch_opportunities()</field>
|
||||
<field name="binding_model_id" ref="customer_itch_cycle.model_itch_cycle_product_partner"/>
|
||||
<field name="binding_type">action</field>
|
||||
<field name="binding_view_types">tree,form</field>
|
||||
</record>
|
||||
|
||||
<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 js_class="cycle_product_partner_list">
|
||||
<tree js_class="cycle_product_partner_list" decoration-bf="state == 'delayed'" decoration-it="state == 'archived'">
|
||||
<field name="partner_id" widget="many2one_avatar"/>
|
||||
<field name="partner_city"/>
|
||||
<field name="partner_country_id" widget="country_flag" optional="hide"/>
|
||||
<field name="product_id" widget="many2one_avatar"/>
|
||||
<field name="product_categ_id" optional="hide"/>
|
||||
<field name="quantity_planned" sum="Total prévu"/>
|
||||
<field name="quantity_of_orders" sum="Total commandé"/>
|
||||
<field name="cycle_duration" widget="duration"/>
|
||||
<field name="date_last_purchase" optional="hide"/>
|
||||
<field name="date_expected" optional="show" string="Date attendue"/>
|
||||
<field name="date_next_follow_up" optional="show" string="Prochain suivi"/>
|
||||
<field name="state" widget="badge" options="{'classes': {'new': 'bg-info', 'on_time': 'bg-success', 'delayed': 'bg-danger', 'archived': 'bg-secondary'}}"/>
|
||||
<field name="active" invisible="1"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_itch_cycle_product_partner_search" model="ir.ui.view">
|
||||
<field name="name">itch.cycle.product.partner.search</field>
|
||||
<field name="model">itch.cycle.product.partner</field>
|
||||
<field name="arch" type="xml">
|
||||
<search>
|
||||
<field name="partner_id"/>
|
||||
<field name="product_id"/>
|
||||
<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 name="state"/>
|
||||
<separator/>
|
||||
<filter string="Archivé" name="inactive" domain="[('active', '=', False)]"/>
|
||||
<separator/>
|
||||
<filter string="Suivi cette semaine" name="follow_up_this_week"
|
||||
domain="[('date_next_follow_up', '>=', context_today().strftime('%Y-%m-%d')),
|
||||
('date_next_follow_up', '<=', (context_today() + datetime.timedelta(days=7)).strftime('%Y-%m-%d'))]"/>
|
||||
<filter string="Commande attendue cette semaine" name="expected_this_week"
|
||||
domain="[('date_expected', '>=', context_today().strftime('%Y-%m-%d')),
|
||||
('date_expected', '<=', (context_today() + datetime.timedelta(days=7)).strftime('%Y-%m-%d'))]"/>
|
||||
<group expand="0" string="Grouper par">
|
||||
<filter string="État" name="group_by_state" context="{'group_by': 'state'}"/>
|
||||
<filter string="Partenaire" name="group_by_partner" context="{'group_by': 'partner_id'}"/>
|
||||
<filter string="Ville" name="group_by_city" context="{'group_by': 'partner_id:city'}"/>
|
||||
<filter string="Pays" name="group_by_country" context="{'group_by': 'partner_id:country_id'}"/>
|
||||
<filter string="Produit" name="group_by_product" context="{'group_by': 'product_id'}"/>
|
||||
<filter string="Catégorie de produit" name="group_by_product_categ" context="{'group_by': 'product_id:categ_id'}"/>
|
||||
<filter string="Date de suivi" name="group_by_follow_up" context="{'group_by': 'date_next_follow_up:week'}"/>
|
||||
<filter string="Date attendue" name="group_by_expected" context="{'group_by': 'date_expected:week'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
|
@ -23,44 +76,86 @@
|
|||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<header>
|
||||
<field name="prediction_status" widget="statusbar" options="{'on_time': 'green', 'delayed': 'red', 'archived': 'gray'}"/>
|
||||
<field name="state" widget="statusbar" statusbar_visible="new,on_time,delayed,archived"/>
|
||||
</header>
|
||||
<sheet>
|
||||
<div class="oe_button_box" name="button_box">
|
||||
<widget name="web_ribbon" title="Archivé" bg_color="bg-secondary" invisible="active"/>
|
||||
<button name="toggle_active"
|
||||
type="object"
|
||||
class="oe_stat_button"
|
||||
icon="fa-archive"
|
||||
help="Archiver/Désarchiver cet enregistrement">
|
||||
<field name="active" widget="boolean_button" options="{'terminology': 'archive'}"/>
|
||||
</button>
|
||||
<button name="action_create_opportunity"
|
||||
type="object"
|
||||
string="Créer une opportunité"
|
||||
class="oe_stat_button"
|
||||
icon="fa-plus"
|
||||
help="Créer une nouvelle opportunité pour ce partenaire et ce produit"/>
|
||||
<button name="action_view_latest_opportunity"
|
||||
type="object"
|
||||
string="Dernière opportunité"
|
||||
class="oe_stat_button"
|
||||
icon="fa-eye"
|
||||
help="Afficher la dernière opportunité créée pour ce partenaire et ce produit"/>
|
||||
</div>
|
||||
<group>
|
||||
<group string="Informations principales">
|
||||
<field name="partner_id"/>
|
||||
<field name="product_id"/>
|
||||
<group string="Surchage manuelle">
|
||||
<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 string="Partenaire" col="2">
|
||||
<field name="partner_id"
|
||||
context="{'form_view_ref': 'base.view_partner_form'}"
|
||||
options="{'always_reload': true}"
|
||||
help="Partenaire associé à ce cycle"/>
|
||||
<field name="partner_email" help="Adresse e-mail principale du partenaire"/>
|
||||
<field name="partner_phone" help="Téléphone principal du partenaire"/>
|
||||
<field name="partner_mobile" help="Téléphone mobile du partenaire"/>
|
||||
<field name="partner_city" help="Ville du partenaire"/>
|
||||
<field name="partner_country_id" help="Pays du partenaire"/>
|
||||
</group>
|
||||
<group string="Statistiques">
|
||||
<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 string="Produit" col="2">
|
||||
<field name="product_id"
|
||||
context="{'form_view_ref': 'product.product_normal_form_view'}"
|
||||
options="{'always_reload': true}"
|
||||
help="Produit associé à ce cycle"/>
|
||||
<field name="product_categ_id" help="Catégorie de produit"/>
|
||||
<field name="product_type" help="Type de produit (stockable, consommable, service)"/>
|
||||
<field name="product_lst_price" widget="monetary" help="Prix de vente du produit"/>
|
||||
<field name="product_qty_available" help="Quantité disponible en stock"/>
|
||||
</group>
|
||||
</group>
|
||||
<group>
|
||||
<group string="Remplacement manuel" col="2">
|
||||
<field name="cycle_duration_override" help="Remplacement manuel de la durée du cycle"/>
|
||||
<field name="quantity_manual_override" help="Remplacement manuel de la quantité prévue"/>
|
||||
<field name="date_expected_override" help="Remplacement manuel de la date attendue"/>
|
||||
</group>
|
||||
<group string="Statut" col="2">
|
||||
<field name="date_expected" readonly="1" widget="date" help="Date attendue calculée"/>
|
||||
<field name="cycle_duration" readonly="1" widget="duration" help="Durée du cycle calculée"/>
|
||||
<field name="quantity_planned" readonly="1" widget="float_time" help="Quantité prévue calculée"/>
|
||||
<field name="state" readonly="1" widget="badge" help="Statut actuel du cycle"/>
|
||||
<field name="active" invisible="1"/>
|
||||
</group>
|
||||
</group>
|
||||
<group>
|
||||
<group string="Statistiques de commande" col="2">
|
||||
<field name="quantity_of_orders" readonly="1" help="Nombre total de commandes"/>
|
||||
<field name="quantity_total_ordered" readonly="1" help="Quantité totale commandée"/>
|
||||
<field name="quantity_min_ordered" readonly="1" help="Quantité minimale commandée"/>
|
||||
<field name="quantity_max_ordered" readonly="1" help="Quantité maximale commandée"/>
|
||||
<field name="quantity_mean_ordered" readonly="1" help="Quantité moyenne commandée"/>
|
||||
</group>
|
||||
<group string="Statistiques de cycle" col="2">
|
||||
<field name="date_last_purchase" readonly="1" widget="date" help="Date de la dernière commande"/>
|
||||
<field name="date_next_follow_up" widget="date" options="{'datepicker': {'minDate': context_today().strftime('%Y-%m-%d')}}" help="Date du prochain suivi"/>
|
||||
<field name="cycle_duration_calculated" readonly="1" widget="duration" help="Durée du cycle calculée"/>
|
||||
<field name="date_expected_evaluated" readonly="1" widget="date" help="Date attendue évaluée"/>
|
||||
</group>
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="Lignes de commandes associées">
|
||||
<field name="sale_order_line_ids">
|
||||
<page string="Lignes de commande associées">
|
||||
<field name="sale_order_line_ids" readonly="1">
|
||||
<tree editable="bottom">
|
||||
<field name="order_id"/>
|
||||
<field name="product_id"/>
|
||||
|
|
@ -71,9 +166,26 @@
|
|||
</tree>
|
||||
</field>
|
||||
</page>
|
||||
<page string="Opportunités associées">
|
||||
<field name="opportunity_ids" readonly="1">
|
||||
<tree>
|
||||
<field name="name"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="expected_revenue"/>
|
||||
<field name="probability"/>
|
||||
<field name="stage_id"/>
|
||||
<field name="create_date"/>
|
||||
</tree>
|
||||
</field>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_follower_ids"/>
|
||||
<field name="activity_ids"/>
|
||||
<field name="message_ids"/>
|
||||
</div>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
</odoo>
|
||||
|
|
|
|||
|
|
@ -1,21 +1,30 @@
|
|||
|
||||
<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="inherit_id" ref="product.product_category_form_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Catégorie de produit">
|
||||
<sheet>
|
||||
<xpath expr="//form[1]/sheet[1]" position="inside">
|
||||
<group string="Sales Cycle">
|
||||
<group>
|
||||
<field name="name"/>
|
||||
<field name="parent_id"/>
|
||||
<field name="is_cycle_tracked"/>
|
||||
<field name="temp_is_cycle_tracked" invisible="1"/>
|
||||
<button name="action_apply_cycle_tracked"
|
||||
string="Apply Changes"
|
||||
type="object"
|
||||
class="btn btn-primary"
|
||||
invisible="is_cycle_tracked == temp_is_cycle_tracked"/>
|
||||
<field name="seasonal_factor"
|
||||
help="Factor used to adjust sales predictions based on seasonal patterns"/>
|
||||
<field name="season_months"
|
||||
widget="many2many_tags"
|
||||
options="{'no_create': True}"
|
||||
help="Months where this product category has peak sales"
|
||||
invisible="not seasonal_factor"
|
||||
required="seasonal_factor"/>
|
||||
</group>
|
||||
<group string="Saisonnalité">
|
||||
<field name="seasonal_factor"/>
|
||||
<field name="season_months"/>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</group>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="view_partner_form_inherit_itch_cycle" model="ir.ui.view">
|
||||
<field name="name">res.partner.form.itch.cycle</field>
|
||||
|
|
@ -6,27 +6,84 @@
|
|||
<field name="inherit_id" ref="base.view_partner_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<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">
|
||||
<field name="itch_next_delay" readonly="1"/>
|
||||
</group>
|
||||
<field name="reorder_cycle_product_ids" readonly="1">
|
||||
<page string="Product Cycles" name="product_cycles">
|
||||
<field name="itch_cycle_product_ids">
|
||||
<tree>
|
||||
<field name="product_id"/>
|
||||
<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"/>
|
||||
<field name="quantity_mean_ordered"/>
|
||||
<field name="date_expected"/>
|
||||
<field name="active"/>
|
||||
</tree>
|
||||
</field>
|
||||
</page>
|
||||
</notebook>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_cycle_product_partner_form" model="ir.ui.view">
|
||||
<field name="name">itch.cycle.product.partner.form</field>
|
||||
<field name="model">itch.cycle.product.partner</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<header>
|
||||
<button name="create_opportunity"
|
||||
string="Create Opportunity"
|
||||
type="object"
|
||||
class="oe_highlight"
|
||||
invisible="not active"/>
|
||||
</header>
|
||||
<sheet>
|
||||
<div class="oe_button_box" name="button_box">
|
||||
<button name="action_view_opportunities"
|
||||
type="object"
|
||||
class="oe_stat_button"
|
||||
icon="fa-star">
|
||||
<field name="opportunity_count" widget="statinfo" string="Opportunities"/>
|
||||
</button>
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<field name="partner_id"/>
|
||||
<field name="product_id"/>
|
||||
<field name="active"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="date_expected"/>
|
||||
<field name="cycle_duration"/>
|
||||
<field name="quantity_mean_ordered"/>
|
||||
<field name="quantity_total_ordered"/>
|
||||
</group>
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="Opportunities" name="opportunities">
|
||||
<field name="opportunity_ids">
|
||||
<tree>
|
||||
<field name="name"/>
|
||||
<field name="date_deadline"/>
|
||||
<field name="expected_revenue"/>
|
||||
<field name="stage_id"/>
|
||||
</tree>
|
||||
</field>
|
||||
</page>
|
||||
<page string="Sales History" name="sales_history">
|
||||
<field name="sale_order_line_ids">
|
||||
<tree>
|
||||
<field name="order_id"/>
|
||||
<field name="product_uom_qty"/>
|
||||
<field name="price_unit"/>
|
||||
<field name="price_subtotal"/>
|
||||
<field name="state"/>
|
||||
</tree>
|
||||
</field>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_follower_ids" groups="base.group_user"/>
|
||||
<field name="activity_ids"/>
|
||||
<field name="message_ids"/>
|
||||
</div>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
|
|||
1
customer_itch_cycle/wizards/__init__.py
Normal file
1
customer_itch_cycle/wizards/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
from . import apply_to_child_categories
|
||||
48
customer_itch_cycle/wizards/apply_to_child_categories.py
Normal file
48
customer_itch_cycle/wizards/apply_to_child_categories.py
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
from odoo import models, fields, api
|
||||
|
||||
class ApplyToChildCategories(models.TransientModel):
|
||||
"""
|
||||
Wizard to apply cycle tracking settings to child categories
|
||||
This wizard is shown when changing is_cycle_tracked on a product category
|
||||
"""
|
||||
_name = 'itch.apply.to.child.categories'
|
||||
_description = 'Apply Settings to Child Categories'
|
||||
|
||||
category_id = fields.Many2one('product.category', string='Category', required=True)
|
||||
new_value = fields.Boolean(string='New Tracking Value')
|
||||
apply_to_children = fields.Boolean(
|
||||
string='Apply to Child Categories',
|
||||
help='If checked, the cycle tracking settings will be applied to all child categories',
|
||||
default=True
|
||||
)
|
||||
child_count = fields.Integer(
|
||||
string='Number of Child Categories',
|
||||
compute='_compute_child_count'
|
||||
)
|
||||
|
||||
@api.depends('category_id')
|
||||
def _compute_child_count(self):
|
||||
"""Compute the number of child categories that would be affected"""
|
||||
for wizard in self:
|
||||
wizard.child_count = self.env['product.category'].search_count([
|
||||
('id', 'child_of', wizard.category_id.id),
|
||||
('id', '!=', wizard.category_id.id)
|
||||
])
|
||||
|
||||
def apply_settings(self):
|
||||
"""Apply the settings to the selected categories"""
|
||||
self.ensure_one()
|
||||
if self.apply_to_children:
|
||||
categories = self.env['product.category'].search([
|
||||
('id', 'child_of', self.category_id.id)
|
||||
])
|
||||
else:
|
||||
categories = self.category_id
|
||||
|
||||
# Apply the settings
|
||||
values = {
|
||||
'is_cycle_tracked': self.new_value,
|
||||
'temp_is_cycle_tracked': self.new_value,
|
||||
}
|
||||
categories.write(values)
|
||||
return {'type': 'ir.actions.act_window_close'}
|
||||
20
customer_itch_cycle/wizards/apply_to_child_categories.xml
Normal file
20
customer_itch_cycle/wizards/apply_to_child_categories.xml
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="view_apply_to_child_categories_form" model="ir.ui.view">
|
||||
<field name="name">itch.apply.to.child.categories.form</field>
|
||||
<field name="model">itch.apply.to.child.categories</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Apply to Child Categories">
|
||||
<group>
|
||||
<field name="category_id" invisible="1"/>
|
||||
<field name="child_count"/>
|
||||
<field name="apply_to_children"/>
|
||||
</group>
|
||||
<footer>
|
||||
<button string="Apply" name="apply_settings" type="object" class="btn-primary" data-hotkey="q"/>
|
||||
<button string="Cancel" class="btn-secondary" special="cancel" data-hotkey="z"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
Loading…
Reference in a new issue