Skip to content

Commit 137fcbf

Browse files
committed
[ADD] salesperson_button_in_pos: added Salesperson selection button in POS UI
- Created a new module salesperson button in pos - Added salesperson id field in posorder model to track salesperson - Created a new button Select Salesperson in POS frontend - Implemented the button in controlbutton xml - Developed select salesperson button js as an OWL component - Patched ControlButtons to include the Salesperson button in POS - Registered assets properly in manifest py for POS UI - Debugged and tested visibility of the button in POS UI
1 parent 7a85bed commit 137fcbf

File tree

14 files changed

+343
-0
lines changed

14 files changed

+343
-0
lines changed

salesperson_button_in_pos/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from . import models
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
'name': 'Salesperson button in POS',
3+
'version': '1.0',
4+
'summary': 'Adding a salesperson button in pos',
5+
'description': """
6+
Adding salesperson button in pos
7+
""",
8+
'author': 'Raghav Agiwal',
9+
'depends': ['point_of_sale', 'hr'],
10+
'data': [
11+
'views/pos_order_view.xml',
12+
'views/employee_form_view.xml',
13+
],
14+
'assets': {
15+
'point_of_sale._assets_pos': [
16+
'salesperson_button_in_pos/static/src/app/**/*',
17+
],
18+
},
19+
'installable': True,
20+
'application': True,
21+
'license': 'LGPL-3'
22+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from . import pos_order
2+
from . import pos_session
3+
from . import hr_employee
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from odoo import models
2+
3+
4+
class HrEmployee(models.Model):
5+
_inherit = "hr.employee"
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from odoo import models, fields
2+
3+
4+
class PosOrder(models.Model):
5+
_inherit = 'pos.order'
6+
7+
salesperson_id = fields.Many2one('hr.employee', string='Salesperson')
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from odoo import api, models
2+
3+
4+
class PosSession(models.Model):
5+
_inherit = "pos.session"
6+
7+
@api.model
8+
def _load_pos_data_models(self, config_id):
9+
data = super()._load_pos_data_models(config_id)
10+
data.append("hr.employee")
11+
return data
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import { Component, useState } from "@odoo/owl";
2+
import { useService } from "@web/core/utils/hooks";
3+
import { Dialog } from "@web/core/dialog/dialog";
4+
import { Input } from "@point_of_sale/app/generic_components/inputs/input/input";
5+
import { usePos } from "@point_of_sale/app/store/pos_hook";
6+
import { _t } from "@web/core/l10n/translation";
7+
import { unaccent } from "@web/core/utils/strings";
8+
import { fuzzyLookup } from "@web/core/utils/search";
9+
import { useHotkey } from "@web/core/hotkeys/hotkey_hook";
10+
import { makeActionAwaitable } from "@point_of_sale/app/store/make_awaitable_dialog";
11+
12+
13+
export class SalespersonList extends Component {
14+
static template = "salesperson_button_in_pos.SalespersonList";
15+
static components = {Dialog, Input };
16+
static props = {
17+
getPayload: { type: Function },
18+
close: { type: Function },
19+
currentSelectedSalesperson: { type: Object },
20+
};
21+
22+
setup(){
23+
this.pos = usePos();
24+
this.dialog = useService("dialog");
25+
this.action=useService("action");
26+
this.ui = useState(useService("ui"));
27+
this.notification = useService("notification");
28+
this.allSalesperson = this.pos.models["hr.employee"]?.getAll();
29+
this.state = useState({
30+
query: "",
31+
selectedSalesPerson: null
32+
});
33+
useHotkey("enter", () => this.onEnter());
34+
this.loadSalespeople();
35+
}
36+
37+
async loadSalespeople() {
38+
this.state.salespeople = await this.getSalespersons();
39+
}
40+
41+
async editSalesperson(salesperson = false) {
42+
try {
43+
const actionProps = salesperson && salesperson.id ? { resId: salesperson.id } : {};
44+
45+
await this.env.services.action.doAction("salesperson_button_in_pos.action_salesperson_create_form_view", {
46+
props: actionProps,
47+
onClose: async () => {
48+
await this.loadSalespeople();
49+
this.props.close();
50+
},
51+
});
52+
} catch (error) {
53+
console.error("Error opening salesperson form:", error);
54+
}
55+
}
56+
57+
get filteredSalespersons() {
58+
if (!this.state.query) {
59+
return this.allSalesperson;
60+
}
61+
return fuzzyLookup(
62+
this.state.query,
63+
this.allSalesperson,
64+
(salesperson) => unaccent(salesperson.name)
65+
);
66+
}
67+
68+
getSalespersons() {
69+
const users = this.pos.models["res.users"].getAll();
70+
const query = this.state.query?.toLowerCase() || "";
71+
return users.filter((u) => u.name?.toLowerCase().includes(query));
72+
}
73+
74+
selectSalesperson(salesperson) {
75+
const currentOrder = this.pos.get_order();
76+
if (!currentOrder) return;
77+
78+
if (this.props.currentSelectedSalesperson?.id === salesperson.id) {
79+
this.props.getPayload(null);
80+
} else {
81+
this.props.getPayload(salesperson);
82+
}
83+
this.props.close();
84+
}
85+
86+
onEnter() {
87+
this.notification.add(_t('No more customer found for "%s".', this.state.query),300);
88+
}
89+
90+
onSearchInput(event) {
91+
this.state.query = event?.target?.value;
92+
}
93+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<templates id="template" xml:space="preserve">
3+
4+
<t t-name="salesperson_button_in_pos.SalespersonList">
5+
<Dialog bodyClass="'salesperson-list overflow-y-auto'" contentClass="'h-100'">
6+
<t t-set-slot="header">
7+
<button t-if="!ui.isSmall" class="btn btn-primary btn-lg lh-lg" role="img" aria-label="Add a salesperson"
8+
t-on-click="() => this.editSalesperson()"
9+
title="Add a salesperson">
10+
Create
11+
</button>
12+
<Input tModel="[state, 'query']" class="'ms-auto'" isSmall="ui.isSmall" placeholder.translate="Search Salespersons..." icon="{type: 'fa', value: 'fa-search'}" autofocus="true" debounceMillis="100" t-on-input="onSearchInput" />
13+
</t>
14+
<table class="table table-hover">
15+
<thead>
16+
<tr>
17+
<th class="py-2">Name</th>
18+
<th class="py-2">Contact</th>
19+
<th class="py-2"></th>
20+
</tr>
21+
</thead>
22+
<tbody>
23+
<t t-if="filteredSalespersons.length !== 0">
24+
<t t-foreach="filteredSalespersons" t-as="salesperson" t-key="salesperson.id">
25+
<tr t-on-click="() => this.selectSalesperson(salesperson)" style="cursor: pointer;">
26+
<td>
27+
<b t-esc="salesperson.name or ''"/>
28+
<div class="company-field text-bg-muted"/>
29+
</td>
30+
<td t-esc="salesperson?.work_contact_id?.email"/>
31+
<td class="salesperson-line-email">
32+
</td>
33+
<td>
34+
<t t-if="this.props.currentSelectedSalesperson?.id === salesperson?.id">
35+
<button t-on-click="() => this.selectSalesperson(salesperson)" class="btn btn-transparent text-danger">
36+
<i class="fa fa-times text-danger"/>
37+
</button>
38+
</t>
39+
</td>
40+
</tr>
41+
</t>
42+
</t>
43+
<t t-else="">
44+
<tr>
45+
<td colspan="4" class="text-center py-3">No Salesperson Found</td>
46+
</tr>
47+
</t>
48+
</tbody>
49+
</table>
50+
<div t-if="state.query" class="search-more-button d-flex justify-content-center my-2">
51+
<button class="btn btn-lg btn-primary" t-on-click="onEnter">Search more</button>
52+
</div>
53+
<t t-set-slot="footer">
54+
<div class="d-flex justify-content-start flex-wrap gap-2 w-100">
55+
<button class="btn btn-secondary btn-lg lh-lg o-default-button" t-on-click="props.close">Discard</button>
56+
</div>
57+
</t>
58+
</Dialog>
59+
</t>
60+
</templates>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/** @odoo-module **/
2+
3+
import { ControlButtons } from "@point_of_sale/app/screens/product_screen/control_buttons/control_buttons";
4+
import { SelectSalespersonButton } from "./select_salesperson_button/select_salesperson_button";
5+
import { patch } from "@web/core/utils/patch";
6+
7+
patch(ControlButtons, {
8+
components: { ...ControlButtons.components, SelectSalespersonButton, }
9+
});
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<templates id="template" xml:space="preserve">
3+
<t t-name="salesperson_button_in_pos.ControlButton" t-inherit="point_of_sale.ControlButtons" t-inherit-mode="extension">
4+
<xpath expr="//OrderlineNoteButton" position="after">
5+
<SelectSalespersonButton/>
6+
</xpath>
7+
</t>
8+
</templates>

0 commit comments

Comments
 (0)