Perubahan desain business category
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
from . import models
|
||||
from .hooks import post_init_hook
|
||||
@@ -0,0 +1,25 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
{
|
||||
"name": "GRT Business Category Base",
|
||||
"summary": "Shared business category master for cross-module references",
|
||||
"description": """
|
||||
Central business category master module.
|
||||
This module provides the shared business category model, user access context,
|
||||
and reusable mixins so CRM, Sales, Purchase, Expense, and Inventory can all
|
||||
reference the same category definitions.
|
||||
""",
|
||||
"author": "PT Gagak Rimang Teknologi",
|
||||
"website": "https://rimang.id",
|
||||
"category": "Tools",
|
||||
"version": "14.0.1.1.0",
|
||||
"depends": ["base", "analytic"],
|
||||
"post_init_hook": "post_init_hook",
|
||||
"data": [
|
||||
"security/ir.model.access.csv",
|
||||
"security/ir.rule.csv",
|
||||
"views/crm_business_category_views.xml",
|
||||
"views/res_users_business_category_views.xml"
|
||||
],
|
||||
"installable": True,
|
||||
"application": False
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo noupdate="1">
|
||||
<record id="crm_business_category_01" model="crm.business.category">
|
||||
<field name="name">Business Category 1</field>
|
||||
<field name="code">BC01</field>
|
||||
<field name="company_id" ref="base.main_company"/>
|
||||
</record>
|
||||
<record id="crm_business_category_02" model="crm.business.category">
|
||||
<field name="name">Business Category 2</field>
|
||||
<field name="code">BC02</field>
|
||||
<field name="company_id" ref="base.main_company"/>
|
||||
</record>
|
||||
<record id="crm_business_category_03" model="crm.business.category">
|
||||
<field name="name">Business Category 3</field>
|
||||
<field name="code">BC03</field>
|
||||
<field name="company_id" ref="base.main_company"/>
|
||||
</record>
|
||||
<record id="crm_business_category_04" model="crm.business.category">
|
||||
<field name="name">Business Category 4</field>
|
||||
<field name="code">BC04</field>
|
||||
<field name="company_id" ref="base.main_company"/>
|
||||
</record>
|
||||
<record id="crm_business_category_05" model="crm.business.category">
|
||||
<field name="name">Business Category 5</field>
|
||||
<field name="code">BC05</field>
|
||||
<field name="company_id" ref="base.main_company"/>
|
||||
</record>
|
||||
<record id="crm_business_category_06" model="crm.business.category">
|
||||
<field name="name">Business Category 6</field>
|
||||
<field name="code">BC06</field>
|
||||
<field name="company_id" ref="base.main_company"/>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -0,0 +1,162 @@
|
||||
from odoo import SUPERUSER_ID, api
|
||||
|
||||
|
||||
SEED_CATEGORIES = [
|
||||
("crm_business_category_01", "Business Category 1", "BC01"),
|
||||
("crm_business_category_02", "Business Category 2", "BC02"),
|
||||
("crm_business_category_03", "Business Category 3", "BC03"),
|
||||
("crm_business_category_04", "Business Category 4", "BC04"),
|
||||
("crm_business_category_05", "Business Category 5", "BC05"),
|
||||
("crm_business_category_06", "Business Category 6", "BC06"),
|
||||
]
|
||||
|
||||
|
||||
SHARED_XMLID_NAMES = [
|
||||
"view_crm_business_category_tree",
|
||||
"view_crm_business_category_form",
|
||||
"action_crm_business_category",
|
||||
"view_res_users_business_category_tree",
|
||||
"view_res_users_business_category_form",
|
||||
"action_res_users_business_category_access",
|
||||
"action_sync_team_business_category_access_server",
|
||||
"menu_crm_business_category",
|
||||
"menu_res_users_business_category_access",
|
||||
]
|
||||
|
||||
|
||||
def _get_xmlid_map(env, module, names):
|
||||
data = env["ir.model.data"].sudo().search(
|
||||
[("module", "=", module), ("name", "in", names)]
|
||||
)
|
||||
return {record.name: record for record in data}
|
||||
|
||||
|
||||
def _ensure_xmlid(env, module, name, model, res_id, noupdate=True):
|
||||
xmlid = env["ir.model.data"].sudo().search(
|
||||
[("module", "=", module), ("name", "=", name)], limit=1
|
||||
)
|
||||
values = {
|
||||
"module": module,
|
||||
"name": name,
|
||||
"model": model,
|
||||
"res_id": res_id,
|
||||
"noupdate": noupdate,
|
||||
}
|
||||
if xmlid:
|
||||
xmlid.write(values)
|
||||
else:
|
||||
env["ir.model.data"].sudo().create(values)
|
||||
|
||||
|
||||
def _cleanup_duplicate_record(env, xmlid_record, keep_res_id):
|
||||
if not xmlid_record or xmlid_record.res_id == keep_res_id:
|
||||
return
|
||||
|
||||
model = env[xmlid_record.model].sudo()
|
||||
duplicate = model.browse(xmlid_record.res_id)
|
||||
if duplicate.exists():
|
||||
duplicate.unlink()
|
||||
|
||||
|
||||
def _migrate_seed_categories(env):
|
||||
company = env.ref("base.main_company")
|
||||
category_model = env["crm.business.category"].sudo()
|
||||
base_xmlids = _get_xmlid_map(
|
||||
env, "grt_business_category_base", [name for name, _, _ in SEED_CATEGORIES]
|
||||
)
|
||||
old_xmlids = _get_xmlid_map(
|
||||
env, "grt_crm_business_category", [name for name, _, _ in SEED_CATEGORIES]
|
||||
)
|
||||
|
||||
for xmlid_name, category_name, code in SEED_CATEGORIES:
|
||||
chosen = False
|
||||
old_xmlid = old_xmlids.get(xmlid_name)
|
||||
base_xmlid = base_xmlids.get(xmlid_name)
|
||||
|
||||
if old_xmlid:
|
||||
chosen = category_model.browse(old_xmlid.res_id)
|
||||
elif base_xmlid:
|
||||
chosen = category_model.browse(base_xmlid.res_id)
|
||||
else:
|
||||
chosen = category_model.search(
|
||||
[("company_id", "=", company.id), "|", ("code", "=", code), ("name", "=", category_name)],
|
||||
limit=1,
|
||||
)
|
||||
|
||||
if not chosen:
|
||||
chosen = category_model.create(
|
||||
{
|
||||
"name": category_name,
|
||||
"code": code,
|
||||
"company_id": company.id,
|
||||
}
|
||||
)
|
||||
|
||||
if not chosen.code:
|
||||
chosen.code = code
|
||||
|
||||
_ensure_xmlid(
|
||||
env, "grt_business_category_base", xmlid_name, "crm.business.category", chosen.id
|
||||
)
|
||||
_ensure_xmlid(
|
||||
env, "grt_crm_business_category", xmlid_name, "crm.business.category", chosen.id
|
||||
)
|
||||
|
||||
_cleanup_duplicate_record(env, base_xmlid, chosen.id)
|
||||
|
||||
|
||||
def _migrate_shared_xmlids(env):
|
||||
base_xmlids = _get_xmlid_map(env, "grt_business_category_base", SHARED_XMLID_NAMES)
|
||||
old_xmlids = _get_xmlid_map(env, "grt_crm_business_category", SHARED_XMLID_NAMES)
|
||||
|
||||
for xmlid_name in SHARED_XMLID_NAMES:
|
||||
old_xmlid = old_xmlids.get(xmlid_name)
|
||||
base_xmlid = base_xmlids.get(xmlid_name)
|
||||
|
||||
canonical = old_xmlid or base_xmlid
|
||||
if not canonical:
|
||||
continue
|
||||
|
||||
_ensure_xmlid(
|
||||
env,
|
||||
"grt_business_category_base",
|
||||
xmlid_name,
|
||||
canonical.model,
|
||||
canonical.res_id,
|
||||
noupdate=canonical.noupdate,
|
||||
)
|
||||
_ensure_xmlid(
|
||||
env,
|
||||
"grt_crm_business_category",
|
||||
xmlid_name,
|
||||
canonical.model,
|
||||
canonical.res_id,
|
||||
noupdate=canonical.noupdate,
|
||||
)
|
||||
|
||||
if old_xmlid and base_xmlid and old_xmlid.res_id != base_xmlid.res_id:
|
||||
_cleanup_duplicate_record(env, base_xmlid, old_xmlid.res_id)
|
||||
|
||||
|
||||
def _cleanup_obsolete_crm_security(env):
|
||||
obsolete_names = [
|
||||
"access_crm_business_category_user",
|
||||
"access_crm_business_category_manager",
|
||||
"crm_business_category_rule_user",
|
||||
"crm_business_category_rule_manager",
|
||||
]
|
||||
obsolete = env["ir.model.data"].sudo().search(
|
||||
[("module", "=", "grt_crm_business_category"), ("name", "in", obsolete_names)]
|
||||
)
|
||||
for record in obsolete:
|
||||
target = env[record.model].sudo().browse(record.res_id)
|
||||
if target.exists():
|
||||
target.unlink()
|
||||
obsolete.unlink()
|
||||
|
||||
|
||||
def post_init_hook(cr, registry):
|
||||
env = api.Environment(cr, SUPERUSER_ID, {})
|
||||
_migrate_seed_categories(env)
|
||||
_migrate_shared_xmlids(env)
|
||||
_cleanup_obsolete_crm_security(env)
|
||||
@@ -0,0 +1,3 @@
|
||||
from . import business_category_mixin
|
||||
from . import crm_business_category
|
||||
from . import res_users
|
||||
@@ -0,0 +1,46 @@
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class BusinessCategoryMixin(models.AbstractModel):
|
||||
_name = "business.category.mixin"
|
||||
_description = "Business Category Mixin"
|
||||
|
||||
@api.model
|
||||
def _default_business_category_id(self):
|
||||
cr = self.env.cr
|
||||
cr.execute(
|
||||
"""
|
||||
SELECT 1
|
||||
FROM information_schema.columns
|
||||
WHERE table_name = 'res_users' AND column_name = 'active_business_category_id'
|
||||
"""
|
||||
)
|
||||
has_active = bool(cr.fetchone())
|
||||
cr.execute(
|
||||
"""
|
||||
SELECT 1
|
||||
FROM information_schema.columns
|
||||
WHERE table_name = 'res_users' AND column_name = 'default_business_category_id'
|
||||
"""
|
||||
)
|
||||
has_default = bool(cr.fetchone())
|
||||
|
||||
if not has_active and not has_default:
|
||||
return False
|
||||
|
||||
user = self.env.user
|
||||
if has_active and user.active_business_category_id.company_id == self.env.company:
|
||||
return user.active_business_category_id.id
|
||||
if has_default and user.default_business_category_id.company_id == self.env.company:
|
||||
return user.default_business_category_id.id
|
||||
return False
|
||||
|
||||
business_category_id = fields.Many2one(
|
||||
"crm.business.category",
|
||||
string="Business Category",
|
||||
default=_default_business_category_id,
|
||||
required=True,
|
||||
ondelete="restrict",
|
||||
index=True,
|
||||
domain="[('company_id', '=', company_id)]",
|
||||
)
|
||||
@@ -0,0 +1,58 @@
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class CrmBusinessCategory(models.Model):
|
||||
_name = "crm.business.category"
|
||||
_description = "Business Category"
|
||||
_order = "name"
|
||||
|
||||
def init(self):
|
||||
self._cr.execute(
|
||||
"""
|
||||
UPDATE crm_business_category
|
||||
SET company_id = %s
|
||||
WHERE company_id IS NULL
|
||||
""",
|
||||
[self.env.ref("base.main_company").id],
|
||||
)
|
||||
|
||||
name = fields.Char(required=True)
|
||||
code = fields.Char()
|
||||
company_id = fields.Many2one(
|
||||
"res.company",
|
||||
string="Company",
|
||||
required=True,
|
||||
default=lambda self: self.env.company,
|
||||
index=True,
|
||||
)
|
||||
active = fields.Boolean(default=True)
|
||||
description = fields.Text()
|
||||
analytic_account_id = fields.Many2one(
|
||||
"account.analytic.account",
|
||||
string="Analytic Account",
|
||||
domain="[('company_id', '=', company_id)]",
|
||||
ondelete="restrict",
|
||||
help="Shared default analytic account used by modules that reference this business category.",
|
||||
)
|
||||
|
||||
_sql_constraints = [
|
||||
(
|
||||
"crm_business_category_name_company_uniq",
|
||||
"unique(name, company_id)",
|
||||
"Business category name must be unique per company.",
|
||||
),
|
||||
]
|
||||
|
||||
@api.constrains("company_id", "analytic_account_id")
|
||||
def _check_analytic_account_company(self):
|
||||
for category in self:
|
||||
if not category.analytic_account_id or not category.company_id:
|
||||
continue
|
||||
if category.analytic_account_id.company_id != category.company_id:
|
||||
raise ValidationError(
|
||||
_(
|
||||
"Business Category '%s' must use an Analytic Account from company '%s'."
|
||||
)
|
||||
% (category.name, category.company_id.name)
|
||||
)
|
||||
@@ -0,0 +1,196 @@
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class ResUsers(models.Model):
|
||||
_inherit = "res.users"
|
||||
|
||||
allowed_business_category_ids = fields.Many2many(
|
||||
"crm.business.category",
|
||||
"res_users_crm_business_category_rel",
|
||||
"user_id",
|
||||
"business_category_id",
|
||||
string="Allowed Business Categories",
|
||||
domain="[('company_id', 'in', company_ids)]",
|
||||
help="Users can only access records in these business categories.",
|
||||
)
|
||||
team_business_category_ids = fields.Many2many(
|
||||
"crm.business.category",
|
||||
string="Team Business Categories",
|
||||
compute="_compute_effective_business_category_ids",
|
||||
readonly=True,
|
||||
help="Business categories inherited automatically from team membership.",
|
||||
)
|
||||
effective_business_category_ids = fields.Many2many(
|
||||
"crm.business.category",
|
||||
string="Effective Business Categories",
|
||||
compute="_compute_effective_business_category_ids",
|
||||
readonly=True,
|
||||
help="Union of manual access and team-based access.",
|
||||
)
|
||||
|
||||
default_business_category_id = fields.Many2one(
|
||||
"crm.business.category",
|
||||
string="Default Business Category",
|
||||
domain="[('id', 'in', effective_business_category_ids), ('company_id', 'in', company_ids)]",
|
||||
help="Default category for new records.",
|
||||
)
|
||||
|
||||
active_business_category_id = fields.Many2one(
|
||||
"crm.business.category",
|
||||
string="Active Business Category",
|
||||
domain="[('id', 'in', effective_business_category_ids), ('company_id', 'in', company_ids)]",
|
||||
help="Current user context category, similar to active company.",
|
||||
)
|
||||
|
||||
def _build_or_domain(self, domain_parts):
|
||||
if not domain_parts:
|
||||
return []
|
||||
if len(domain_parts) == 1:
|
||||
return domain_parts
|
||||
return ["|"] * (len(domain_parts) - 1) + domain_parts
|
||||
|
||||
def _get_team_business_categories_from_model(self, model_name, domain_parts):
|
||||
self.ensure_one()
|
||||
if model_name not in self.env:
|
||||
return self.env["crm.business.category"]
|
||||
|
||||
team_model = self.env[model_name].sudo()
|
||||
user_id = self._origin.id or self.id
|
||||
if not isinstance(user_id, int):
|
||||
return self.env["crm.business.category"]
|
||||
valid_domain_parts = [
|
||||
domain_part
|
||||
for domain_part in domain_parts
|
||||
if isinstance(domain_part, (list, tuple))
|
||||
and len(domain_part) >= 1
|
||||
and domain_part[0] in team_model._fields
|
||||
]
|
||||
if not valid_domain_parts:
|
||||
return self.env["crm.business.category"]
|
||||
|
||||
domain = self._build_or_domain(valid_domain_parts)
|
||||
teams = team_model.search(domain + [("company_id", "in", self.company_ids.ids)])
|
||||
return teams.mapped("business_category_id")
|
||||
|
||||
def _get_team_business_categories(self):
|
||||
self.ensure_one()
|
||||
categories = self.env["crm.business.category"]
|
||||
|
||||
categories |= self._get_team_business_categories_from_model(
|
||||
"crm.team",
|
||||
[
|
||||
("user_id", "=", self.id),
|
||||
("member_ids", "in", self.id),
|
||||
("sale_team_leader_id", "=", self.id),
|
||||
("team_members_ids", "in", self.id),
|
||||
],
|
||||
)
|
||||
categories |= self._get_team_business_categories_from_model(
|
||||
"purchase.team",
|
||||
[("user_id", "=", self.id), ("member_ids", "in", self.id)],
|
||||
)
|
||||
categories |= self._get_team_business_categories_from_model(
|
||||
"expense.team",
|
||||
[("user_id", "=", self.id), ("member_ids", "in", self.id)],
|
||||
)
|
||||
categories |= self._get_team_business_categories_from_model(
|
||||
"stock.team",
|
||||
[("user_id", "=", self.id), ("member_ids", "in", self.id)],
|
||||
)
|
||||
return categories
|
||||
|
||||
def _filter_business_categories_by_company(self, categories):
|
||||
self.ensure_one()
|
||||
return categories.filtered(lambda category: category.company_id in self.company_ids)
|
||||
|
||||
@api.depends("allowed_business_category_ids", "company_ids")
|
||||
def _compute_effective_business_category_ids(self):
|
||||
for user in self:
|
||||
team_categories = user._get_team_business_categories()
|
||||
allowed_categories = user._filter_business_categories_by_company(
|
||||
user.allowed_business_category_ids
|
||||
)
|
||||
user.team_business_category_ids = team_categories
|
||||
user.effective_business_category_ids = allowed_categories | team_categories
|
||||
|
||||
@api.onchange("allowed_business_category_ids", "company_ids")
|
||||
def _onchange_allowed_business_category_ids(self):
|
||||
for user in self:
|
||||
effective_categories = user._filter_business_categories_by_company(
|
||||
user.allowed_business_category_ids
|
||||
) | user._get_team_business_categories()
|
||||
if user.default_business_category_id not in effective_categories:
|
||||
user.default_business_category_id = False
|
||||
if user.active_business_category_id not in effective_categories:
|
||||
user.active_business_category_id = False
|
||||
if effective_categories and not user.default_business_category_id:
|
||||
user.default_business_category_id = effective_categories[0]
|
||||
if effective_categories and not user.active_business_category_id:
|
||||
user.active_business_category_id = user.default_business_category_id
|
||||
|
||||
@api.constrains(
|
||||
"allowed_business_category_ids",
|
||||
"default_business_category_id",
|
||||
"active_business_category_id",
|
||||
)
|
||||
def _check_business_category_consistency(self):
|
||||
for user in self:
|
||||
effective_categories = user._filter_business_categories_by_company(
|
||||
user.allowed_business_category_ids
|
||||
) | user._get_team_business_categories()
|
||||
if (
|
||||
user.default_business_category_id
|
||||
and user.default_business_category_id not in effective_categories
|
||||
):
|
||||
raise ValidationError(
|
||||
_("Default Business Category must be included in Effective Business Categories.")
|
||||
)
|
||||
if (
|
||||
user.default_business_category_id
|
||||
and user.default_business_category_id.company_id not in user.company_ids
|
||||
):
|
||||
raise ValidationError(
|
||||
_("Default Business Category must belong to one of the user's allowed companies.")
|
||||
)
|
||||
if (
|
||||
user.active_business_category_id
|
||||
and user.active_business_category_id not in effective_categories
|
||||
):
|
||||
raise ValidationError(
|
||||
_("Active Business Category must be included in Effective Business Categories.")
|
||||
)
|
||||
if (
|
||||
user.active_business_category_id
|
||||
and user.active_business_category_id.company_id not in user.company_ids
|
||||
):
|
||||
raise ValidationError(
|
||||
_("Active Business Category must belong to one of the user's allowed companies.")
|
||||
)
|
||||
|
||||
def action_sync_team_business_category_access(self):
|
||||
for user in self:
|
||||
effective_categories = user._filter_business_categories_by_company(
|
||||
user.allowed_business_category_ids
|
||||
) | user._get_team_business_categories()
|
||||
first_category = effective_categories[:1]
|
||||
vals = {}
|
||||
|
||||
if user.default_business_category_id not in effective_categories:
|
||||
vals["default_business_category_id"] = first_category.id if first_category else False
|
||||
elif not user.default_business_category_id and first_category:
|
||||
vals["default_business_category_id"] = first_category.id
|
||||
|
||||
default_id = vals.get("default_business_category_id") or user.default_business_category_id.id
|
||||
if user.active_business_category_id not in effective_categories:
|
||||
vals["active_business_category_id"] = default_id or (
|
||||
first_category.id if first_category else False
|
||||
)
|
||||
elif not user.active_business_category_id:
|
||||
vals["active_business_category_id"] = default_id or (
|
||||
first_category.id if first_category else False
|
||||
)
|
||||
|
||||
if vals:
|
||||
user.write(vals)
|
||||
return {"type": "ir.actions.client", "tag": "reload"}
|
||||
@@ -0,0 +1,3 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_crm_business_category_user,crm.business.category.user,model_crm_business_category,base.group_user,1,0,0,0
|
||||
access_crm_business_category_manager,crm.business.category.manager,model_crm_business_category,base.group_system,1,1,1,1
|
||||
|
@@ -0,0 +1,3 @@
|
||||
id,name,model_id:id,domain_force,groups:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
crm_business_category_rule_user,Business Category by user effective list,model_crm_business_category,"[('company_id','in',user.company_ids.ids),('id','in',user.effective_business_category_ids.ids)]",base.group_user,1,0,0,0
|
||||
crm_business_category_rule_manager,Business Category full access for system admin,model_crm_business_category,"[('company_id','in',user.company_ids.ids)]",base.group_system,1,1,1,1
|
||||
|
@@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="view_crm_business_category_tree" model="ir.ui.view">
|
||||
<field name="name">crm.business.category.tree</field>
|
||||
<field name="model">crm.business.category</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree>
|
||||
<field name="name"/>
|
||||
<field name="code"/>
|
||||
<field name="company_id"/>
|
||||
<field name="active"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_crm_business_category_form" model="ir.ui.view">
|
||||
<field name="name">crm.business.category.form</field>
|
||||
<field name="model">crm.business.category</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<sheet>
|
||||
<group>
|
||||
<field name="name"/>
|
||||
<field name="code"/>
|
||||
<field name="company_id"/>
|
||||
<field name="active"/>
|
||||
</group>
|
||||
<group string="Accounting">
|
||||
<field name="analytic_account_id" domain="[('company_id', '=', company_id)]"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="description"/>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_crm_business_category" model="ir.actions.act_window">
|
||||
<field name="name">Business Categories</field>
|
||||
<field name="res_model">crm.business.category</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
</record>
|
||||
|
||||
<menuitem id="menu_crm_business_category"
|
||||
name="Business Categories"
|
||||
parent="base.menu_administration"
|
||||
action="action_crm_business_category"
|
||||
sequence="85"
|
||||
groups="base.group_system"/>
|
||||
</odoo>
|
||||
@@ -0,0 +1,77 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="view_res_users_business_category_tree" model="ir.ui.view">
|
||||
<field name="name">res.users.business.category.tree</field>
|
||||
<field name="model">res.users</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree>
|
||||
<button name="action_sync_team_business_category_access" type="object" string="Sync" class="btn-link"/>
|
||||
<field name="name"/>
|
||||
<field name="login"/>
|
||||
<field name="active"/>
|
||||
<field name="company_id"/>
|
||||
<field name="team_business_category_ids" widget="many2many_tags"/>
|
||||
<field name="allowed_business_category_ids" widget="many2many_tags"/>
|
||||
<field name="effective_business_category_ids" widget="many2many_tags"/>
|
||||
<field name="default_business_category_id"/>
|
||||
<field name="active_business_category_id"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_res_users_business_category_form" model="ir.ui.view">
|
||||
<field name="name">res.users.business.category.form</field>
|
||||
<field name="model">res.users</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="User Business Category Access">
|
||||
<header>
|
||||
<button
|
||||
name="action_sync_team_business_category_access"
|
||||
type="object"
|
||||
string="Sync Team Access"
|
||||
class="oe_highlight"
|
||||
/>
|
||||
</header>
|
||||
<sheet>
|
||||
<group>
|
||||
<field name="name"/>
|
||||
<field name="login"/>
|
||||
<field name="active"/>
|
||||
<field name="company_id"/>
|
||||
<field name="company_ids" widget="many2many_tags"/>
|
||||
</group>
|
||||
<group string="Business Category Access">
|
||||
<field name="team_business_category_ids" widget="many2many_tags" readonly="1"/>
|
||||
<field name="allowed_business_category_ids" widget="many2many_tags" domain="[('company_id', 'in', company_ids)]"/>
|
||||
<field name="effective_business_category_ids" widget="many2many_tags" readonly="1"/>
|
||||
<field name="default_business_category_id" domain="[('id', 'in', effective_business_category_ids), ('company_id', 'in', company_ids)]"/>
|
||||
<field name="active_business_category_id" domain="[('id', 'in', effective_business_category_ids), ('company_id', 'in', company_ids)]"/>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_res_users_business_category_access" model="ir.actions.act_window">
|
||||
<field name="name">User Business Category Access</field>
|
||||
<field name="res_model">res.users</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="domain">[('share', '=', False)]</field>
|
||||
</record>
|
||||
|
||||
<record id="action_sync_team_business_category_access_server" model="ir.actions.server">
|
||||
<field name="name">Sync Team Category Access</field>
|
||||
<field name="model_id" ref="base.model_res_users"/>
|
||||
<field name="binding_model_id" ref="base.model_res_users"/>
|
||||
<field name="binding_view_types">list,form</field>
|
||||
<field name="state">code</field>
|
||||
<field name="code">records.action_sync_team_business_category_access()</field>
|
||||
</record>
|
||||
|
||||
<menuitem id="menu_res_users_business_category_access"
|
||||
name="User Category Access"
|
||||
parent="base.menu_administration"
|
||||
action="action_res_users_business_category_access"
|
||||
sequence="86"
|
||||
groups="base.group_system"/>
|
||||
</odoo>
|
||||
@@ -9,23 +9,20 @@ This enables different staging flow per business category through team pipelines
|
||||
"author": "PT Gagak Rimang Teknologi",
|
||||
"website": "https://rimang.id",
|
||||
"category": "Sales/CRM",
|
||||
"version": "14.0.2.10.1",
|
||||
"depends": ["crm"],
|
||||
"version": "14.0.2.11.0",
|
||||
"depends": ["crm", "grt_business_category_base"],
|
||||
"data": [
|
||||
"security/ir.model.access.csv",
|
||||
"security/ir.rule.csv",
|
||||
"data/crm_business_category_data.xml",
|
||||
"views/assets.xml",
|
||||
"views/crm_activity_history_views.xml",
|
||||
"views/crm_business_category_views.xml",
|
||||
"views/crm_lead_views.xml",
|
||||
"views/crm_pipeline_report_views.xml",
|
||||
"views/crm_menu_views.xml",
|
||||
"views/crm_stage_views.xml",
|
||||
"views/crm_team_views.xml",
|
||||
"views/crm_team_business_category_views.xml",
|
||||
"views/mail_activity_views.xml",
|
||||
"views/res_users_business_category_views.xml",
|
||||
"views/mail_activity_views.xml"
|
||||
],
|
||||
"installable": True,
|
||||
"application": False,
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
from . import crm_business_category
|
||||
from . import business_category_mixin
|
||||
from . import crm_team
|
||||
from . import crm_stage
|
||||
from . import crm_lead
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class ResUsers(models.Model):
|
||||
@@ -10,175 +9,3 @@ class ResUsers(models.Model):
|
||||
last_gps_client_time = fields.Char(string="Last GPS Client Time", readonly=True)
|
||||
last_gps_client_tz = fields.Char(string="Last GPS Client TZ", readonly=True)
|
||||
last_gps_recorded_at = fields.Datetime(string="Last GPS Recorded At", readonly=True)
|
||||
|
||||
allowed_business_category_ids = fields.Many2many(
|
||||
"crm.business.category",
|
||||
"res_users_crm_business_category_rel",
|
||||
"user_id",
|
||||
"business_category_id",
|
||||
string="Allowed Business Categories",
|
||||
domain="[('company_id', 'in', company_ids)]",
|
||||
help="Users can only access CRM data in these business categories.",
|
||||
)
|
||||
team_business_category_ids = fields.Many2many(
|
||||
"crm.business.category",
|
||||
string="Team Business Categories",
|
||||
compute="_compute_effective_business_category_ids",
|
||||
readonly=True,
|
||||
help="Business categories inherited automatically from Sales Team membership.",
|
||||
)
|
||||
effective_business_category_ids = fields.Many2many(
|
||||
"crm.business.category",
|
||||
string="Effective Business Categories",
|
||||
compute="_compute_effective_business_category_ids",
|
||||
readonly=True,
|
||||
help="Union of manual access and Sales Team-based access.",
|
||||
)
|
||||
|
||||
default_business_category_id = fields.Many2one(
|
||||
"crm.business.category",
|
||||
string="Default Business Category",
|
||||
domain="[('id', 'in', effective_business_category_ids), ('company_id', 'in', company_ids)]",
|
||||
help="Default category for new records.",
|
||||
)
|
||||
|
||||
active_business_category_id = fields.Many2one(
|
||||
"crm.business.category",
|
||||
string="Active Business Category",
|
||||
domain="[('id', 'in', effective_business_category_ids), ('company_id', 'in', company_ids)]",
|
||||
help="Current user context category, similar to active company.",
|
||||
)
|
||||
|
||||
def _build_or_domain(self, domain_parts):
|
||||
if not domain_parts:
|
||||
return []
|
||||
if len(domain_parts) == 1:
|
||||
return domain_parts
|
||||
return ["|"] * (len(domain_parts) - 1) + domain_parts
|
||||
|
||||
def _get_team_business_categories_from_model(self, model_name, domain_parts):
|
||||
self.ensure_one()
|
||||
if model_name not in self.env:
|
||||
return self.env["crm.business.category"]
|
||||
|
||||
team_model = self.env[model_name].sudo()
|
||||
user_id = self._origin.id or self.id
|
||||
if not isinstance(user_id, int):
|
||||
return self.env["crm.business.category"]
|
||||
valid_domain_parts = [
|
||||
domain_part
|
||||
for domain_part in domain_parts
|
||||
if isinstance(domain_part, (list, tuple))
|
||||
and len(domain_part) >= 1
|
||||
and domain_part[0] in team_model._fields
|
||||
]
|
||||
if not valid_domain_parts:
|
||||
return self.env["crm.business.category"]
|
||||
|
||||
domain = self._build_or_domain(valid_domain_parts)
|
||||
teams = team_model.search(domain + [("company_id", "in", self.company_ids.ids)])
|
||||
return teams.mapped("business_category_id")
|
||||
|
||||
def _get_team_business_categories(self):
|
||||
self.ensure_one()
|
||||
categories = self.env["crm.business.category"]
|
||||
|
||||
categories |= self._get_team_business_categories_from_model(
|
||||
"crm.team",
|
||||
[
|
||||
("user_id", "=", self.id),
|
||||
("member_ids", "in", self.id),
|
||||
("sale_team_leader_id", "=", self.id),
|
||||
("team_members_ids", "in", self.id),
|
||||
],
|
||||
)
|
||||
categories |= self._get_team_business_categories_from_model(
|
||||
"purchase.team",
|
||||
[("user_id", "=", self.id), ("member_ids", "in", self.id)],
|
||||
)
|
||||
categories |= self._get_team_business_categories_from_model(
|
||||
"expense.team",
|
||||
[("user_id", "=", self.id), ("member_ids", "in", self.id)],
|
||||
)
|
||||
categories |= self._get_team_business_categories_from_model(
|
||||
"stock.team",
|
||||
[("user_id", "=", self.id), ("member_ids", "in", self.id)],
|
||||
)
|
||||
return categories
|
||||
|
||||
def _filter_business_categories_by_company(self, categories):
|
||||
self.ensure_one()
|
||||
return categories.filtered(lambda category: category.company_id in self.company_ids)
|
||||
|
||||
@api.depends("allowed_business_category_ids", "company_ids")
|
||||
def _compute_effective_business_category_ids(self):
|
||||
for user in self:
|
||||
team_categories = user._get_team_business_categories()
|
||||
allowed_categories = user._filter_business_categories_by_company(user.allowed_business_category_ids)
|
||||
user.team_business_category_ids = team_categories
|
||||
user.effective_business_category_ids = allowed_categories | team_categories
|
||||
|
||||
@api.onchange("allowed_business_category_ids", "company_ids")
|
||||
def _onchange_allowed_business_category_ids(self):
|
||||
for user in self:
|
||||
effective_categories = user._filter_business_categories_by_company(
|
||||
user.allowed_business_category_ids
|
||||
) | user._get_team_business_categories()
|
||||
if user.default_business_category_id not in effective_categories:
|
||||
user.default_business_category_id = False
|
||||
if user.active_business_category_id not in effective_categories:
|
||||
user.active_business_category_id = False
|
||||
if effective_categories and not user.default_business_category_id:
|
||||
user.default_business_category_id = effective_categories[0]
|
||||
if effective_categories and not user.active_business_category_id:
|
||||
user.active_business_category_id = user.default_business_category_id
|
||||
|
||||
@api.constrains(
|
||||
"allowed_business_category_ids",
|
||||
"default_business_category_id",
|
||||
"active_business_category_id",
|
||||
)
|
||||
def _check_business_category_consistency(self):
|
||||
for user in self:
|
||||
effective_categories = user._filter_business_categories_by_company(
|
||||
user.allowed_business_category_ids
|
||||
) | user._get_team_business_categories()
|
||||
if user.default_business_category_id and user.default_business_category_id not in effective_categories:
|
||||
raise ValidationError(
|
||||
_("Default Business Category must be included in Effective Business Categories.")
|
||||
)
|
||||
if user.default_business_category_id and user.default_business_category_id.company_id not in user.company_ids:
|
||||
raise ValidationError(
|
||||
_("Default Business Category must belong to one of the user's allowed companies.")
|
||||
)
|
||||
if user.active_business_category_id and user.active_business_category_id not in effective_categories:
|
||||
raise ValidationError(
|
||||
_("Active Business Category must be included in Effective Business Categories.")
|
||||
)
|
||||
if user.active_business_category_id and user.active_business_category_id.company_id not in user.company_ids:
|
||||
raise ValidationError(
|
||||
_("Active Business Category must belong to one of the user's allowed companies.")
|
||||
)
|
||||
|
||||
def action_sync_team_business_category_access(self):
|
||||
for user in self:
|
||||
effective_categories = user._filter_business_categories_by_company(
|
||||
user.allowed_business_category_ids
|
||||
) | user._get_team_business_categories()
|
||||
first_category = effective_categories[:1]
|
||||
vals = {}
|
||||
|
||||
if user.default_business_category_id not in effective_categories:
|
||||
vals["default_business_category_id"] = first_category.id if first_category else False
|
||||
elif not user.default_business_category_id and first_category:
|
||||
vals["default_business_category_id"] = first_category.id
|
||||
|
||||
default_id = vals.get("default_business_category_id") or user.default_business_category_id.id
|
||||
if user.active_business_category_id not in effective_categories:
|
||||
vals["active_business_category_id"] = default_id or (first_category.id if first_category else False)
|
||||
elif not user.active_business_category_id:
|
||||
vals["active_business_category_id"] = default_id or (first_category.id if first_category else False)
|
||||
|
||||
if vals:
|
||||
user.write(vals)
|
||||
return {"type": "ir.actions.client", "tag": "reload"}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_crm_business_category_user,crm.business.category.user,model_crm_business_category,crm.group_use_lead,1,0,0,0
|
||||
access_crm_business_category_manager,crm.business.category.manager,model_crm_business_category,sales_team.group_sale_manager,1,1,1,1
|
||||
access_crm_activity_history_user,crm.activity.history.user,model_crm_activity_history,crm.group_use_lead,1,0,0,0
|
||||
access_crm_activity_history_manager,crm.activity.history.manager,model_crm_activity_history,sales_team.group_sale_manager,1,1,1,1
|
||||
access_crm_activity_history_sysadmin,crm.activity.history.sysadmin,model_crm_activity_history,base.group_system,1,1,1,1
|
||||
|
||||
|
@@ -2,10 +2,8 @@ id,name,model_id:id,domain_force,groups:id,perm_read,perm_write,perm_create,perm
|
||||
crm_lead_business_category_rule_user_read_create,CRM Lead read/create by effective business category,crm.model_crm_lead,"[('company_id','in',user.company_ids.ids),('business_category_id','in',user.effective_business_category_ids.ids)]",crm.group_use_lead,1,0,1,0
|
||||
crm_lead_business_category_rule_user_write_own,CRM Lead write own by effective business category,crm.model_crm_lead,"[('company_id','in',user.company_ids.ids),('business_category_id','in',user.effective_business_category_ids.ids),'|',('user_id','=',user.id),('create_uid','=',user.id)]",crm.group_use_lead,0,1,0,1
|
||||
crm_team_business_category_rule_user,CRM Team read by effective business category,sales_team.model_crm_team,"[('company_id','in',user.company_ids.ids),('business_category_id','in',user.effective_business_category_ids.ids)]",crm.group_use_lead,1,0,0,0
|
||||
crm_business_category_rule_user,Business Category by user effective list,model_crm_business_category,"[('company_id','in',user.company_ids.ids),('id','in',user.effective_business_category_ids.ids)]",crm.group_use_lead,1,0,0,0
|
||||
crm_lead_business_category_rule_manager,CRM Lead full access for manager in effective business category,crm.model_crm_lead,"[('company_id','in',user.company_ids.ids),('business_category_id','in',user.effective_business_category_ids.ids)]",sales_team.group_sale_manager,1,1,1,1
|
||||
crm_team_business_category_rule_manager,CRM Team full access for manager in effective business category,sales_team.model_crm_team,"[('company_id','in',user.company_ids.ids),('business_category_id','in',user.effective_business_category_ids.ids)]",sales_team.group_sale_manager,1,1,1,1
|
||||
crm_business_category_rule_manager,Business Category access for manager in effective business category,model_crm_business_category,"[('company_id','in',user.company_ids.ids),('id','in',user.effective_business_category_ids.ids)]",sales_team.group_sale_manager,1,1,1,1
|
||||
crm_activity_history_rule_user,CRM Activity History by effective business category,model_crm_activity_history,"[('company_id','in',user.company_ids.ids),('business_category_id','in',user.effective_business_category_ids.ids)]",crm.group_use_lead,1,0,0,0
|
||||
crm_activity_history_rule_manager,CRM Activity History full access for manager in effective business category,model_crm_activity_history,"[('company_id','in',user.company_ids.ids),('business_category_id','in',user.effective_business_category_ids.ids)]",sales_team.group_sale_manager,1,1,1,1
|
||||
crm_team_business_category_rule_sysadmin,CRM Team full access for system admin,sales_team.model_crm_team,"[('company_id','in',user.company_ids.ids)]",base.group_system,1,1,1,1
|
||||
|
||||
|
@@ -10,16 +10,15 @@ for expense transactions and accounting entries.
|
||||
"author": "PT Gagak Rimang Teknologi",
|
||||
"website": "https://rimang.id",
|
||||
"category": "Human Resources/Expenses",
|
||||
"version": "14.0.1.0.0",
|
||||
"version": "14.0.1.1.0",
|
||||
"depends": [
|
||||
"hr_expense",
|
||||
"account",
|
||||
"grt_crm_business_category",
|
||||
"grt_business_category_base",
|
||||
],
|
||||
"data": [
|
||||
"security/ir.model.access.csv",
|
||||
"security/ir.rule.csv",
|
||||
"views/crm_business_category_views.xml",
|
||||
"views/expense_team_views.xml",
|
||||
"views/hr_expense_views.xml",
|
||||
"views/expense_report_business_category_views.xml",
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class CrmBusinessCategory(models.Model):
|
||||
@@ -8,20 +7,7 @@ class CrmBusinessCategory(models.Model):
|
||||
expense_analytic_account_id = fields.Many2one(
|
||||
"account.analytic.account",
|
||||
string="Expense Analytic Account",
|
||||
domain="[('company_id', '=', company_id)]",
|
||||
ondelete="restrict",
|
||||
help="Default analytic account used for expense transactions in this business category.",
|
||||
related="analytic_account_id",
|
||||
readonly=False,
|
||||
help="Alias to the shared analytic account used for this business category.",
|
||||
)
|
||||
|
||||
@api.constrains("company_id", "expense_analytic_account_id")
|
||||
def _check_expense_analytic_account_company(self):
|
||||
for category in self:
|
||||
if not category.expense_analytic_account_id or not category.company_id:
|
||||
continue
|
||||
if category.expense_analytic_account_id.company_id != category.company_id:
|
||||
raise ValidationError(
|
||||
_(
|
||||
"Business Category '%s' must use an Expense Analytic Account from company '%s'."
|
||||
)
|
||||
% (category.name, category.company_id.name)
|
||||
)
|
||||
|
||||
@@ -9,9 +9,9 @@ hr_expense_sheet_business_category_rule_sysadmin,Expense Report full access for
|
||||
expense_team_business_category_rule_user,Expense Team read by effective business category,model_expense_team,"[('company_id','in',user.company_ids.ids),('business_category_id','in',user.effective_business_category_ids.ids)]",hr_expense.group_hr_expense_user,1,0,0,0
|
||||
expense_team_business_category_rule_manager,Expense Team full access for expense manager in effective business category,model_expense_team,"[('company_id','in',user.company_ids.ids),('business_category_id','in',user.effective_business_category_ids.ids)]",hr_expense.group_hr_expense_manager,1,1,1,1
|
||||
expense_team_business_category_rule_sysadmin,Expense Team full access for system admin,model_expense_team,"[('company_id','in',user.company_ids.ids)]",base.group_system,1,1,1,1
|
||||
crm_business_category_rule_expense_user,Business Category by user effective list for expense user,grt_crm_business_category.model_crm_business_category,"[('company_id','in',user.company_ids.ids),('id','in',user.effective_business_category_ids.ids)]",hr_expense.group_hr_expense_user,1,0,0,0
|
||||
crm_business_category_rule_expense_manager,Business Category access for expense manager in effective business category,grt_crm_business_category.model_crm_business_category,"[('company_id','in',user.company_ids.ids),('id','in',user.effective_business_category_ids.ids)]",hr_expense.group_hr_expense_manager,1,1,1,1
|
||||
crm_business_category_rule_expense_sysadmin,Business Category full access for system admin,grt_crm_business_category.model_crm_business_category,"[('company_id','in',user.company_ids.ids)]",base.group_system,1,1,1,1
|
||||
crm_business_category_rule_expense_user,Business Category by user effective list for expense user,grt_business_category_base.model_crm_business_category,"[('company_id','in',user.company_ids.ids),('id','in',user.effective_business_category_ids.ids)]",hr_expense.group_hr_expense_user,1,0,0,0
|
||||
crm_business_category_rule_expense_manager,Business Category access for expense manager in effective business category,grt_business_category_base.model_crm_business_category,"[('company_id','in',user.company_ids.ids),('id','in',user.effective_business_category_ids.ids)]",hr_expense.group_hr_expense_manager,1,1,1,1
|
||||
crm_business_category_rule_expense_sysadmin,Business Category full access for system admin,grt_business_category_base.model_crm_business_category,"[('company_id','in',user.company_ids.ids)]",base.group_system,1,1,1,1
|
||||
account_move_expense_business_category_rule_user,Journal Entry read by effective business category for expense user,account.model_account_move,"[('company_id','in',user.company_ids.ids),('expense_business_category_id','in',user.effective_business_category_ids.ids)]",hr_expense.group_hr_expense_user,1,0,0,0
|
||||
account_move_expense_business_category_rule_manager,Journal Entry access for expense manager in effective business category,account.model_account_move,"[('company_id','in',user.company_ids.ids),('expense_business_category_id','in',user.effective_business_category_ids.ids)]",hr_expense.group_hr_expense_manager,1,1,1,1
|
||||
account_move_line_expense_business_category_rule_user,Journal Item read by effective business category for expense user,account.model_account_move_line,"[('company_id','in',user.company_ids.ids),('move_id.expense_business_category_id','in',user.effective_business_category_ids.ids)]",hr_expense.group_hr_expense_user,1,0,0,0
|
||||
|
||||
|
@@ -3,7 +3,7 @@
|
||||
<record id="view_crm_business_category_form_expense_analytic" model="ir.ui.view">
|
||||
<field name="name">crm.business.category.form.expense.analytic</field>
|
||||
<field name="model">crm.business.category</field>
|
||||
<field name="inherit_id" ref="grt_crm_business_category.view_crm_business_category_form"/>
|
||||
<field name="inherit_id" ref="grt_business_category_base.view_crm_business_category_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='description']/.." position="before">
|
||||
<group string="Expenses">
|
||||
|
||||
@@ -11,6 +11,6 @@
|
||||
name="Business Categories"
|
||||
parent="hr_expense.menu_hr_expense_root"
|
||||
sequence="46"
|
||||
action="grt_crm_business_category.action_crm_business_category"
|
||||
action="grt_business_category_base.action_crm_business_category"
|
||||
groups="hr_expense.group_hr_expense_manager"/>
|
||||
</odoo>
|
||||
|
||||
@@ -10,19 +10,18 @@ propagation for stock valuation journal entries.
|
||||
"author": "PT Gagak Rimang Teknologi",
|
||||
"website": "https://rimang.id",
|
||||
"category": "Inventory",
|
||||
"version": "14.0.1.0.0",
|
||||
"version": "14.0.1.1.1",
|
||||
"depends": [
|
||||
"stock_account",
|
||||
"sale_stock",
|
||||
"purchase_stock",
|
||||
"grt_crm_business_category",
|
||||
"grt_business_category_base",
|
||||
"grt_sales_business_category",
|
||||
"grt_purchase_business_category",
|
||||
],
|
||||
"data": [
|
||||
"security/ir.model.access.csv",
|
||||
"security/ir.rule.csv",
|
||||
"views/crm_business_category_views.xml",
|
||||
"views/stock_team_views.xml",
|
||||
"views/stock_warehouse_views.xml",
|
||||
"views/stock_picking_views.xml",
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class CrmBusinessCategory(models.Model):
|
||||
@@ -8,20 +7,7 @@ class CrmBusinessCategory(models.Model):
|
||||
inventory_analytic_account_id = fields.Many2one(
|
||||
"account.analytic.account",
|
||||
string="Inventory Analytic Account",
|
||||
domain="[('company_id', '=', company_id)]",
|
||||
ondelete="restrict",
|
||||
help="Default analytic account used for stock valuation entries in this business category.",
|
||||
related="analytic_account_id",
|
||||
readonly=False,
|
||||
help="Alias to the shared analytic account used for this business category.",
|
||||
)
|
||||
|
||||
@api.constrains("company_id", "inventory_analytic_account_id")
|
||||
def _check_inventory_analytic_account_company(self):
|
||||
for category in self:
|
||||
if not category.inventory_analytic_account_id or not category.company_id:
|
||||
continue
|
||||
if category.inventory_analytic_account_id.company_id != category.company_id:
|
||||
raise ValidationError(
|
||||
_(
|
||||
"Business Category '%s' must use an Inventory Analytic Account from company '%s'."
|
||||
)
|
||||
% (category.name, category.company_id.name)
|
||||
)
|
||||
|
||||
@@ -9,10 +9,10 @@ class ProductTemplate(models.Model):
|
||||
"crm.business.category",
|
||||
string="Business Category",
|
||||
default=lambda self: self.env["business.category.mixin"]._default_business_category_id(),
|
||||
required=True,
|
||||
ondelete="restrict",
|
||||
index=True,
|
||||
domain="[('company_id', '=', company_id)]",
|
||||
help="Shared business category for operational products. System-generated products may leave this empty.",
|
||||
)
|
||||
|
||||
@api.onchange("business_category_id")
|
||||
|
||||
@@ -12,9 +12,9 @@ stock_warehouse_business_category_rule_sysadmin,Warehouse full access for system
|
||||
stock_team_business_category_rule_user,Inventory Team read by effective business category,model_stock_team,"[('company_id','in',user.company_ids.ids),('business_category_id','in',user.effective_business_category_ids.ids)]",stock.group_stock_user,1,0,0,0
|
||||
stock_team_business_category_rule_manager,Inventory Team full access for inventory manager in effective business category,model_stock_team,"[('company_id','in',user.company_ids.ids),('business_category_id','in',user.effective_business_category_ids.ids)]",stock.group_stock_manager,1,1,1,1
|
||||
stock_team_business_category_rule_sysadmin,Inventory Team full access for system admin,model_stock_team,"[('company_id','in',user.company_ids.ids)]",base.group_system,1,1,1,1
|
||||
crm_business_category_rule_inventory_user,Business Category by user effective list for inventory user,grt_crm_business_category.model_crm_business_category,"[('company_id','in',user.company_ids.ids),('id','in',user.effective_business_category_ids.ids)]",stock.group_stock_user,1,0,0,0
|
||||
crm_business_category_rule_inventory_manager,Business Category access for inventory manager in effective business category,grt_crm_business_category.model_crm_business_category,"[('company_id','in',user.company_ids.ids),('id','in',user.effective_business_category_ids.ids)]",stock.group_stock_manager,1,1,1,1
|
||||
crm_business_category_rule_inventory_sysadmin,Business Category full access for system admin,grt_crm_business_category.model_crm_business_category,"[('company_id','in',user.company_ids.ids)]",base.group_system,1,1,1,1
|
||||
crm_business_category_rule_inventory_user,Business Category by user effective list for inventory user,grt_business_category_base.model_crm_business_category,"[('company_id','in',user.company_ids.ids),('id','in',user.effective_business_category_ids.ids)]",stock.group_stock_user,1,0,0,0
|
||||
crm_business_category_rule_inventory_manager,Business Category access for inventory manager in effective business category,grt_business_category_base.model_crm_business_category,"[('company_id','in',user.company_ids.ids),('id','in',user.effective_business_category_ids.ids)]",stock.group_stock_manager,1,1,1,1
|
||||
crm_business_category_rule_inventory_sysadmin,Business Category full access for system admin,grt_business_category_base.model_crm_business_category,"[('company_id','in',user.company_ids.ids)]",base.group_system,1,1,1,1
|
||||
account_move_inventory_business_category_rule_user,Journal Entry read by effective business category for inventory user,account.model_account_move,"[('company_id','in',user.company_ids.ids),('inventory_business_category_id','in',user.effective_business_category_ids.ids)]",stock.group_stock_user,1,0,0,0
|
||||
account_move_inventory_business_category_rule_manager,Journal Entry access for inventory manager in effective business category,account.model_account_move,"[('company_id','in',user.company_ids.ids),('inventory_business_category_id','in',user.effective_business_category_ids.ids)]",stock.group_stock_manager,1,1,1,1
|
||||
account_move_line_inventory_business_category_rule_user,Journal Item read by effective business category for inventory user,account.model_account_move_line,"[('company_id','in',user.company_ids.ids),('move_id.inventory_business_category_id','in',user.effective_business_category_ids.ids)]",stock.group_stock_user,1,0,0,0
|
||||
|
||||
|
@@ -3,7 +3,7 @@
|
||||
<record id="view_crm_business_category_form_inventory_analytic" model="ir.ui.view">
|
||||
<field name="name">crm.business.category.form.inventory.analytic</field>
|
||||
<field name="model">crm.business.category</field>
|
||||
<field name="inherit_id" ref="grt_crm_business_category.view_crm_business_category_form"/>
|
||||
<field name="inherit_id" ref="grt_business_category_base.view_crm_business_category_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='description']/.." position="before">
|
||||
<group string="Inventory">
|
||||
|
||||
@@ -11,6 +11,6 @@
|
||||
name="Business Categories"
|
||||
parent="stock.menu_stock_root"
|
||||
sequence="46"
|
||||
action="grt_crm_business_category.action_crm_business_category"
|
||||
action="grt_business_category_base.action_crm_business_category"
|
||||
groups="stock.group_stock_manager"/>
|
||||
</odoo>
|
||||
|
||||
@@ -10,16 +10,15 @@ for purchasing transactions.
|
||||
"author": "PT Gagak Rimang Teknologi",
|
||||
"website": "https://rimang.id",
|
||||
"category": "Purchases",
|
||||
"version": "14.0.1.0.0",
|
||||
"version": "14.0.1.1.0",
|
||||
"depends": [
|
||||
"purchase",
|
||||
"account",
|
||||
"grt_crm_business_category",
|
||||
"grt_business_category_base",
|
||||
],
|
||||
"data": [
|
||||
"security/ir.model.access.csv",
|
||||
"security/ir.rule.csv",
|
||||
"views/crm_business_category_views.xml",
|
||||
"views/purchase_team_views.xml",
|
||||
"views/purchase_order_views.xml",
|
||||
"views/purchase_report_business_category_views.xml",
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class CrmBusinessCategory(models.Model):
|
||||
@@ -8,20 +7,7 @@ class CrmBusinessCategory(models.Model):
|
||||
purchase_analytic_account_id = fields.Many2one(
|
||||
"account.analytic.account",
|
||||
string="Purchase Analytic Account",
|
||||
domain="[('company_id', '=', company_id)]",
|
||||
ondelete="restrict",
|
||||
help="Default analytic account used for purchasing transactions in this business category.",
|
||||
related="analytic_account_id",
|
||||
readonly=False,
|
||||
help="Alias to the shared analytic account used for this business category.",
|
||||
)
|
||||
|
||||
@api.constrains("company_id", "purchase_analytic_account_id")
|
||||
def _check_purchase_analytic_account_company(self):
|
||||
for category in self:
|
||||
if not category.purchase_analytic_account_id or not category.company_id:
|
||||
continue
|
||||
if category.purchase_analytic_account_id.company_id != category.company_id:
|
||||
raise ValidationError(
|
||||
_(
|
||||
"Business Category '%s' must use a Purchase Analytic Account from company '%s'."
|
||||
)
|
||||
% (category.name, category.company_id.name)
|
||||
)
|
||||
|
||||
@@ -6,8 +6,8 @@ purchase_order_business_category_rule_sysadmin,Purchase Order full access for sy
|
||||
purchase_team_business_category_rule_user,Purchase Team read by effective business category,model_purchase_team,"[('company_id','in',user.company_ids.ids),('business_category_id','in',user.effective_business_category_ids.ids)]",purchase.group_purchase_user,1,0,0,0
|
||||
purchase_team_business_category_rule_manager,Purchase Team full access for purchase manager in effective business category,model_purchase_team,"[('company_id','in',user.company_ids.ids),('business_category_id','in',user.effective_business_category_ids.ids)]",purchase.group_purchase_manager,1,1,1,1
|
||||
purchase_team_business_category_rule_sysadmin,Purchase Team full access for system admin,model_purchase_team,"[('company_id','in',user.company_ids.ids)]",base.group_system,1,1,1,1
|
||||
crm_business_category_rule_purchase_user,Business Category by user effective list for purchase user,grt_crm_business_category.model_crm_business_category,"[('company_id','in',user.company_ids.ids),('id','in',user.effective_business_category_ids.ids)]",purchase.group_purchase_user,1,0,0,0
|
||||
crm_business_category_rule_purchase_manager,Business Category access for purchase manager in effective business category,grt_crm_business_category.model_crm_business_category,"[('company_id','in',user.company_ids.ids),('id','in',user.effective_business_category_ids.ids)]",purchase.group_purchase_manager,1,1,1,1
|
||||
crm_business_category_rule_purchase_user,Business Category by user effective list for purchase user,grt_business_category_base.model_crm_business_category,"[('company_id','in',user.company_ids.ids),('id','in',user.effective_business_category_ids.ids)]",purchase.group_purchase_user,1,0,0,0
|
||||
crm_business_category_rule_purchase_manager,Business Category access for purchase manager in effective business category,grt_business_category_base.model_crm_business_category,"[('company_id','in',user.company_ids.ids),('id','in',user.effective_business_category_ids.ids)]",purchase.group_purchase_manager,1,1,1,1
|
||||
account_move_purchase_business_category_rule_user,Vendor Bill read by effective business category for purchase user,account.model_account_move,"[('company_id','in',user.company_ids.ids),('purchase_business_category_id','in',user.effective_business_category_ids.ids)]",purchase.group_purchase_user,1,0,0,0
|
||||
account_move_purchase_business_category_rule_manager,Vendor Bill access for purchase manager in effective business category,account.model_account_move,"[('company_id','in',user.company_ids.ids),('purchase_business_category_id','in',user.effective_business_category_ids.ids)]",purchase.group_purchase_manager,1,1,1,1
|
||||
account_move_line_purchase_business_category_rule_user,Journal Item read by effective business category for purchase user,account.model_account_move_line,"[('company_id','in',user.company_ids.ids),('move_id.purchase_business_category_id','in',user.effective_business_category_ids.ids)]",purchase.group_purchase_user,1,0,0,0
|
||||
|
||||
|
@@ -3,7 +3,7 @@
|
||||
<record id="view_crm_business_category_form_purchase_analytic" model="ir.ui.view">
|
||||
<field name="name">crm.business.category.form.purchase.analytic</field>
|
||||
<field name="model">crm.business.category</field>
|
||||
<field name="inherit_id" ref="grt_crm_business_category.view_crm_business_category_form"/>
|
||||
<field name="inherit_id" ref="grt_business_category_base.view_crm_business_category_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='description']/.." position="before">
|
||||
<group string="Purchasing">
|
||||
|
||||
@@ -11,6 +11,6 @@
|
||||
name="Business Categories"
|
||||
parent="purchase.menu_purchase_root"
|
||||
sequence="46"
|
||||
action="grt_crm_business_category.action_crm_business_category"
|
||||
action="grt_business_category_base.action_crm_business_category"
|
||||
groups="purchase.group_purchase_manager"/>
|
||||
</odoo>
|
||||
|
||||
@@ -9,12 +9,13 @@ plus a two-step approval flow: Sales Team Leader then Accounting Manager.
|
||||
"author": "PT Gagak Rimang Teknologi",
|
||||
"website": "https://rimang.id",
|
||||
"category": "Sales/Sales",
|
||||
"version": "14.0.1.0.14",
|
||||
"version": "14.0.1.1.0",
|
||||
"depends": [
|
||||
"sale_management",
|
||||
"sale_crm",
|
||||
"account",
|
||||
"contacts",
|
||||
"grt_business_category_base",
|
||||
"grt_crm_business_category",
|
||||
],
|
||||
"post_init_hook": "post_init_hook",
|
||||
@@ -23,7 +24,6 @@ plus a two-step approval flow: Sales Team Leader then Accounting Manager.
|
||||
"security/security.xml",
|
||||
"security/ir.model.access.csv",
|
||||
"security/ir.rule.csv",
|
||||
"views/crm_business_category_views.xml",
|
||||
"views/customer_behavior_dashboard_views.xml",
|
||||
"views/customer_behavior_recompute_wizard_views.xml",
|
||||
"views/customer_behavior_segment_views.xml",
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
from . import crm_team
|
||||
from . import crm_business_category
|
||||
from . import customer_behavior_segment
|
||||
from . import customer_behavior_config
|
||||
from . import customer_behavior_analysis
|
||||
|
||||
@@ -5,8 +5,8 @@ sale_order_business_category_rule_manager,Sale Order full access for sales manag
|
||||
sale_order_business_category_rule_sysadmin,Sale Order full access for system admin,sale.model_sale_order,"[(1,'=',1)]",base.group_system,1,1,1,1
|
||||
crm_team_business_category_rule_sales_user,CRM Team read by effective business category for sales user,sales_team.model_crm_team,"[('business_category_id','in',user.effective_business_category_ids.ids)]",sales_team.group_sale_salesman,1,0,0,0
|
||||
crm_team_business_category_rule_sales_manager,CRM Team full access for sales manager in effective business category,sales_team.model_crm_team,"[('company_id','in',user.company_ids.ids),('business_category_id','in',user.effective_business_category_ids.ids)]",sales_team.group_sale_manager,1,1,1,1
|
||||
crm_business_category_rule_sales_user,Business Category by user effective list for sales user,grt_crm_business_category.model_crm_business_category,"[('id','in',user.effective_business_category_ids.ids)]",sales_team.group_sale_salesman,1,0,0,0
|
||||
crm_business_category_rule_sales_manager,Business Category access for sales manager in effective business category,grt_crm_business_category.model_crm_business_category,"[('company_id','in',user.company_ids.ids),('id','in',user.effective_business_category_ids.ids)]",sales_team.group_sale_manager,1,1,1,1
|
||||
crm_business_category_rule_sales_user,Business Category by user effective list for sales user,grt_business_category_base.model_crm_business_category,"[('id','in',user.effective_business_category_ids.ids)]",sales_team.group_sale_salesman,1,0,0,0
|
||||
crm_business_category_rule_sales_manager,Business Category access for sales manager in effective business category,grt_business_category_base.model_crm_business_category,"[('company_id','in',user.company_ids.ids),('id','in',user.effective_business_category_ids.ids)]",sales_team.group_sale_manager,1,1,1,1
|
||||
customer_behavior_analysis_rule_sales_user,Customer Behavior Analysis by effective business category,model_customer_behavior_analysis,"[('business_category_id','in',user.effective_business_category_ids.ids)]",sales_team.group_sale_salesman,1,0,0,0
|
||||
customer_behavior_analysis_rule_sales_manager,Customer Behavior Analysis full access for sales manager in effective business category,model_customer_behavior_analysis,"[('business_category_id.company_id','in',user.company_ids.ids),('business_category_id','in',user.effective_business_category_ids.ids)]",sales_team.group_sale_manager,1,1,1,1
|
||||
account_move_business_category_rule_sales_user,Invoice read by effective business category for sales user,account.model_account_move,"[('company_id','in',user.company_ids.ids),('business_category_id','in',user.effective_business_category_ids.ids)]",sales_team.group_sale_salesman,1,0,0,0
|
||||
|
||||
|
@@ -3,7 +3,7 @@
|
||||
<record id="view_crm_business_category_form_analytic" model="ir.ui.view">
|
||||
<field name="name">crm.business.category.form.analytic</field>
|
||||
<field name="model">crm.business.category</field>
|
||||
<field name="inherit_id" ref="grt_crm_business_category.view_crm_business_category_form"/>
|
||||
<field name="inherit_id" ref="grt_business_category_base.view_crm_business_category_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='description']/.." position="before">
|
||||
<group string="Accounting">
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
name="Business Categories"
|
||||
parent="sale.sale_order_menu"
|
||||
sequence="36"
|
||||
action="grt_crm_business_category.action_crm_business_category"
|
||||
action="grt_business_category_base.action_crm_business_category"
|
||||
groups="sales_team.group_sale_manager"/>
|
||||
|
||||
<menuitem id="menu_customer_behavior_analysis"
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
[options]
|
||||
addons_path = C:\odoo14c\server\odoo\addons,C:\addon14
|
||||
logfile = C:\addon14\log\odoo_upgrade_business_category.log
|
||||
log_level = info
|
||||
log_handler = :INFO
|
||||
xmlrpc_port = 8071
|
||||
db_host = localhost
|
||||
db_port = 5432
|
||||
db_user = openpg
|
||||
db_password = openpgpwd
|
||||
pg_path = C:\Odoo16\PostgreSQL\bin
|
||||
Reference in New Issue
Block a user