Skip to content

Commit

Permalink
[IMP] rma: add dashboard
Browse files Browse the repository at this point in the history
  • Loading branch information
sbejaoui committed Oct 17, 2024
1 parent 98ec8b5 commit 70872c5
Show file tree
Hide file tree
Showing 6 changed files with 383 additions and 1 deletion.
1 change: 1 addition & 0 deletions rma/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"views/rma_tag_views.xml",
"views/stock_picking_views.xml",
"views/stock_warehouse_views.xml",
"views/dashboard.xml",
"views/res_config_settings_views.xml",
],
"post_init_hook": "post_init_hook",
Expand Down
109 changes: 108 additions & 1 deletion rma/models/rma_operation.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
# Copyright 2020 Tecnativa - Ernesto Tejeda
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from odoo import fields, models
from ast import literal_eval
from collections import defaultdict

from odoo import _, api, fields, models
from odoo.osv.expression import AND

PROCESSED_STATES = ["received", "refunded", "replaced", "finished"]
AWAITING_ACTION_STATES = ["waiting_return", "waiting_replacement", "confirmed"]


class RmaOperation(models.Model):
Expand All @@ -10,7 +17,107 @@ class RmaOperation(models.Model):

active = fields.Boolean(default=True)
name = fields.Char(required=True, translate=True)
color = fields.Integer()
count_rma_draft = fields.Integer(compute="_compute_count_rma")
count_rma_awaiting_action = fields.Integer(compute="_compute_count_rma")
count_rma_processed = fields.Integer(compute="_compute_count_rma")

_sql_constraints = [
("name_uniq", "unique (name)", "That operation name already exists !"),
]

@api.model
def _get_rma_draft_domain(self):
return [("state", "=", "draft")]

@api.model
def _get_rma_awaiting_action_domain(self):
return [("state", "in", AWAITING_ACTION_STATES)]

@api.model
def _get_rma_processed_domain(self):
return [("state", "in", PROCESSED_STATES)]

def _compute_count_rma(self):
self.update(
{
"count_rma_draft": 0,
"count_rma_processed": 0,
"count_rma_awaiting_action": 0,
}
)
state_by_op = defaultdict(int)
for group in self.env["rma"].read_group(
AND([[("operation_id", "!=", False)]]),
groupby=["operation_id", "state"],
fields=["id"],
lazy=False,
):
operation_id = group.get("operation_id")[0]
state = group.get("state")
count = group.get("__count")
if state == "draft":
state_by_op[(operation_id, "count_rma_draft")] += count
if state in PROCESSED_STATES:
state_by_op[(operation_id, "count_rma_processed")] += count
if state in AWAITING_ACTION_STATES:
state_by_op[(operation_id, "count_rma_awaiting_action")] += count
for (operation_id, field), count in state_by_op.items():
self.browse(operation_id).update({field: count})

def _get_action(self, name, domain):
action = self.env["ir.actions.actions"]._for_xml_id("rma.rma_action")
action["display_name"] = name
context = {
"search_default_operation_id": [self.id],
"default_operation_id": self.id,
}
action_context = literal_eval(action["context"])
context = {**action_context, **context}
action["context"] = context
action["domain"] = domain
return action

def get_action_rma_tree_draft(self):
self.ensure_one()
name = self.display_name + ": " + _("Draft")
return self._get_action(
name,
domain=AND(
[
[("operation_id", "=", self.id)],
self._get_rma_draft_domain(),
]
),
)

def get_action_rma_tree_awaiting_action(self):
self.ensure_one()
name = self.display_name + ": " + _("Awaiting Action")
return self._get_action(
name,
domain=AND(
[
[("operation_id", "=", self.id)],
self._get_rma_awaiting_action_domain(),
]
),
)

def get_action_rma_tree_processed(self):
self.ensure_one()
name = self.display_name + ": " + _("Processed")
return self._get_action(
name,
domain=AND(
[
[("operation_id", "=", self.id)],
self._get_rma_processed_domain(),
]
),
)

def get_action_all_rma(self):
self.ensure_one()
name = self.display_name
return self._get_action(name, domain=[("operation_id", "=", self.id)])

Check warning on line 123 in rma/models/rma_operation.py

View check run for this annotation

Codecov / codecov/patch

rma/models/rma_operation.py#L121-L123

Added lines #L121 - L123 were not covered by tests
1 change: 1 addition & 0 deletions rma/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from . import test_rma
from . import test_rma_dashboard
73 changes: 73 additions & 0 deletions rma/tests/test_rma_dashboard.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# Copyright 2024 ACSONE SA/NV
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from .test_rma import TestRma

PROCESSED_STATES = ["received", "refunded", "replaced", "finished"]
AWAITING_ACTION_STATES = ["waiting_return", "waiting_replacement", "confirmed"]


class TestRmaDashboard(TestRma):
def test_0(self):
operation_replace = self.env.ref("rma.rma_operation_replace")
operation_return = self.env.ref("rma.rma_operation_return")
operation_refund = self.env.ref("rma.rma_operation_refund")
replace_draft_1 = self._create_rma(
self.partner, self.product, 1, self.rma_loc, operation=operation_replace
)
self._create_rma(
self.partner, self.product, 1, self.rma_loc, operation=operation_replace
) # replace_draft_2
replace_draft_1.copy({"state": "confirmed"}) # replace_confirmed
replace_draft_1.copy({"state": "received"}) # replace_received
replace_draft_1.copy({"state": "waiting_return"}) # replace_waiting_return
replace_draft_1.copy( # replace_waiting_replacement
{"state": "waiting_replacement"}
)
return_draft = self._create_rma(
self.partner, self.product, 1, self.rma_loc, operation=operation_return
)
return_draft.copy({"state": "confirmed"}) # return_confirmed
return_draft.copy({"state": "waiting_return"}) # return_waiting_return
return_draft.copy({"state": "returned"}) # return_returned
return_draft.copy({"state": "finished"}) # return_finished
refund_draft = self._create_rma(
self.partner, self.product, 1, self.rma_loc, operation=operation_refund
)
refund_draft.copy({"state": "finished"}) # refund_refunded

self.assertEqual(operation_replace.count_rma_draft, 2)
self.assertEqual(operation_replace.count_rma_awaiting_action, 3)
self.assertEqual(operation_replace.count_rma_processed, 1)

self.assertEqual(operation_return.count_rma_draft, 1)
self.assertEqual(operation_return.count_rma_awaiting_action, 2)
self.assertEqual(operation_return.count_rma_processed, 1)

self.assertEqual(operation_refund.count_rma_draft, 1)
self.assertEqual(operation_refund.count_rma_awaiting_action, 0)
self.assertEqual(operation_refund.count_rma_processed, 1)

action = operation_replace.get_action_rma_tree_draft()
self.assertListEqual(
["&", ("operation_id", "=", operation_replace.id), ("state", "=", "draft")],
action.get("domain"),
)
action = operation_replace.get_action_rma_tree_awaiting_action()
self.assertListEqual(
[
"&",
("operation_id", "=", operation_replace.id),
("state", "in", AWAITING_ACTION_STATES),
],
action.get("domain"),
)
action = operation_replace.get_action_rma_tree_processed()
self.assertListEqual(
[
"&",
("operation_id", "=", operation_replace.id),
("state", "in", PROCESSED_STATES),
],
action.get("domain"),
)
188 changes: 188 additions & 0 deletions rma/views/dashboard.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2024 ACSONE SA/NV
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -->
<odoo>

<record id="rma_operation_kanban" model="ir.ui.view">
<field name="model">rma.operation</field>
<field name="arch" type="xml">
<kanban
class="oe_background_grey o_kanban_dashboard o_emphasize_colors"
create="0"
group_create="false"
>
<field name="color" />
<field name="count_rma_draft" />
<field name="count_rma_awaiting_action" />
<field name="count_rma_processed" />
<templates>
<t t-name="kanban-box">
<div
t-attf-class="#{!selection_mode ? kanban_color(record.color.raw_value) : ''}"
name="rma_operation"
>
<div t-attf-class="o_kanban_card_header">
<div class="o_kanban_card_header_title">
<div class="o_primary" t-if="!selection_mode">
<a type="object" name="get_action_all_rma">
<field name="name" />
</a>
</div>
<div class="o_primary" t-if="selection_mode">
<field name="name" />
</div>
</div>
<div
class="o_kanban_manage_button_section"
t-if="!selection_mode"
>
<a class="o_kanban_manage_toggle_button" href="#"><i
class="fa fa-ellipsis-v"
role="img"
aria-label="Manage"
title="Manage"
/></a>
</div>
</div>
<div
class="container o_kanban_card_content"
t-if="!selection_mode"
>
<div class="row">
<div class="col-9 o_kanban_primary_left">
<button
class="btn btn-link"
name="get_action_rma_tree_draft"
type="object"
>
<span>Draft</span>
</button>
</div>
<div class="col-3 o_kanban_primary_right">
<button
class="btn btn-link"
name="get_action_rma_tree_draft"
type="object"
>
<span
t-esc="record.count_rma_draft.value"
/>
</button>
</div>
<div class="col-9 o_kanban_primary_left">
<button
class="btn btn-link"
name="get_action_rma_tree_awaiting_action"
type="object"
>
<span>Awaiting action</span>
</button>
</div>
<div class="col-3 o_kanban_primary_right">
<button
class="btn btn-link"
name="get_action_rma_tree_awaiting_action"
type="object"
>
<span
t-esc="record.count_rma_awaiting_action.value"
/>
</button>
</div>
<div class="col-9 o_kanban_primary_left">
<button
class="btn btn-link"
name="get_action_rma_tree_processed"
type="object"
>
<span>Processed</span>
</button>
</div>
<div class="col-3 o_kanban_primary_right">
<button
class="btn btn-link"
name="get_action_rma_tree_processed"
type="object"
>
<span
t-esc="record.count_rma_processed.value"
/>
</button>
</div>
</div>
</div>
<div
class="container o_kanban_card_manage_pane dropdown-menu"
role="menu"
>
<div class="row">
<div
class="col-6 o_kanban_card_manage_section o_kanban_manage_new"
>
<div
role="menuitem"
class="o_kanban_card_manage_title"
>
<span>New</span>
</div>
<div role="menuitem">
<a
name="%(action_rma_form)d"
type="action"
>RMA</a>
</div>
</div>
</div>

<div
t-if="widget.editable"
class="o_kanban_card_manage_settings row"
>
<div
class="col-8"
role="menuitem"
aria-haspopup="true"
>
<ul
class="oe_kanban_colorpicker"
data-field="color"
role="menu"
/>
</div>
<div role="menuitem" class="col-4">
<a
class="dropdown-item"
role="menuitem"
type="edit"
>Configuration</a>
</div>
</div>
</div>
</div>
</t>
</templates>
</kanban>
</field>
</record>

<record id="rma_dashboard_action" model="ir.actions.act_window">
<field name="name">RMA Overview</field>
<field name="res_model">rma.operation</field>
<field name="type">ir.actions.act_window</field>
<field name="view_mode">kanban,form</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face"> Create a new RMA operation </p>
<p>The RMA operation system allows you to configure each return operation
with specific settings that will adjust its behavior.</p>
</field>
</record>

<menuitem
action="rma_dashboard_action"
id="rma_dashboard_menu"
parent="rma_menu"
sequence="0"
name="Overview"
/>

</odoo>
Loading

0 comments on commit 70872c5

Please sign in to comment.