diff --git a/addons/point_of_sale/models/pos_session.py b/addons/point_of_sale/models/pos_session.py index 396265e7334..e32b8446dad 100644 --- a/addons/point_of_sale/models/pos_session.py +++ b/addons/point_of_sale/models/pos_session.py @@ -1756,7 +1756,7 @@ class PosSession(models.Model): 'domain': [], 'fields': [ 'name', 'street', 'city', 'state_id', 'country_id', 'vat', 'lang', 'phone', 'zip', 'mobile', 'email', - 'barcode', 'write_date', 'property_account_position_id', 'property_product_pricelist' + 'barcode', 'write_date', 'property_account_position_id', 'property_product_pricelist', 'parent_name' ], }, } diff --git a/addons/point_of_sale/static/src/css/pos.css b/addons/point_of_sale/static/src/css/pos.css index a17b3dd7226..f8f05bed4f5 100644 --- a/addons/point_of_sale/static/src/css/pos.css +++ b/addons/point_of_sale/static/src/css/pos.css @@ -898,6 +898,10 @@ td { flex-wrap: wrap; gap: 2px; } +.pos .product-list-container .product-list-empty p { + margin: 10px; + text-align: center; +} @media screen and (max-width: 768px) { .pos .product-list { padding: 0px; @@ -1798,12 +1802,46 @@ td { } -/* e) The Client List Screen */ +/* e) The Partner List Screen */ .pos .partnerlist-screen .full-content{ overflow: auto; } +.pos .no-results-message { + text-align: center; +} + +.pos .no-results-message { + margin-top: 45px; + padding: 0 10%; +} + +.pos .no-results-message .button { + margin: auto; + width: 180px; + border-radius: 25px; + padding: 10px 0; + cursor: pointer; + font-size: large; + box-shadow: 0px -2px 5px -3px rgb(82,82,82) inset; + transition: all linear 75ms; +} + +.pos .no-results-message .button:hover { + opacity: 0.8; +} + +.pos .no-results-message .button:active { + opacity: 0.5; +} + +.pos .partnerlist-screen .top-content div.top-right-buttons { + display: flex; + justify-content: flex-end; + flex: 1; +} + .pos .partnerlist-screen .partner-list{ font-size: 16px; width: 100%; @@ -1813,6 +1851,30 @@ td { padding: 12px 8px; } +.pos .partnerlist-screen .fa.fa-phone, +.pos .partnerlist-screen .fa.fa-paper-plane-o { + margin-right: 4px; +} + +.pos .partnerlist-screen .e-mail-field { + white-space: nowrap; +} + +.pos .partnerlist-screen tr div.company-field { + color: #888c8f; +} + +.pos .partnerlist-screen tr.highlight div.company-field { + color: white; +} + +.pos .partnerlist-screen tr.highlight div.unselect-tag { + font-size: large; + color:#555555; + font-weight: bold; + padding-top: 5px; +} + .pos .partnerlist-screen .partner-list th.partner-line-last-column-placeholder, .pos .partnerlist-screen .partner-list td.partner-line-last-column-placeholder { padding: 0; @@ -1822,22 +1884,31 @@ td { transition: all 150ms linear; background: rgb(230,230,230); } +.pos .partnerlist-screen tr.partner-line:hover { + cursor: pointer; +} .pos .partnerlist-screen .partner-list thead > tr, .pos .partnerlist-screen .partner-list tr:nth-child(even) { - background: rgb(247,247,247); + background: rgb(247,247,247,0.1); } .pos .partnerlist-screen .partner-list tr.highlight{ - transition: all 150ms linear; - background: rgb(110,200,155) !important; + background: rgb(110,200,155); color: white; } -.pos .partnerlist-screen .partner-list tr.lowlight{ - transition: all 150ms linear; - background: rgb(216, 238, 227); +.pos .partnerlist-screen .partner-list tr.highlight:active { + background-color: black; + color: white; } -.pos .partnerlist-screen .partner-list tr.lowlight:nth-child(even){ - transition: all 150ms linear; - background: rgb(227, 246, 237); +.pos .partnerlist-screen tr.partner-line:hover { + cursor: pointer; + background: rgb(207, 238, 222); +} +.pos .partnerlist-screen tr.partner-line:active { + background-color: black; + color: white; +} +.pos .edit-partner-button:hover { + background: #c7c7c7; } .pos .partner-line { vertical-align: text-top; @@ -1846,6 +1917,10 @@ td { margin-top: 6px; color: black; } +.pos .partner-list tr.partner-line.highlight{ + background: rgb(110,200,155); + color: white; +} .pos .partnerlist-screen .partner-details{ padding: 16px; } diff --git a/addons/point_of_sale/static/src/js/Screens/PartnerListScreen/PartnerLine.js b/addons/point_of_sale/static/src/js/Screens/PartnerListScreen/PartnerLine.js index 3f7395dd576..17a01a548ad 100644 --- a/addons/point_of_sale/static/src/js/Screens/PartnerListScreen/PartnerLine.js +++ b/addons/point_of_sale/static/src/js/Screens/PartnerListScreen/PartnerLine.js @@ -6,7 +6,14 @@ odoo.define('point_of_sale.PartnerLine', function(require) { class PartnerLine extends PosComponent { get highlight() { - return this.props.partner !== this.props.selectedPartner ? '' : 'highlight'; + return this._isPartnerSelected ? 'highlight' : ''; + } + get shortAddress() { + const { partner } = this.props; + return [partner.zip, partner.city, partner.state_id[1]].filter(field => field).join(', '); + } + get _isPartnerSelected() { + return this.props.partner === this.props.selectedPartner; } } PartnerLine.template = 'PartnerLine'; diff --git a/addons/point_of_sale/static/src/js/Screens/PartnerListScreen/PartnerListScreen.js b/addons/point_of_sale/static/src/js/Screens/PartnerListScreen/PartnerListScreen.js index a731a5d394f..25908e422ba 100644 --- a/addons/point_of_sale/static/src/js/Screens/PartnerListScreen/PartnerListScreen.js +++ b/addons/point_of_sale/static/src/js/Screens/PartnerListScreen/PartnerListScreen.js @@ -29,7 +29,6 @@ odoo.define('point_of_sale.PartnerListScreen', function(require) { setup() { super.setup(); useListener('click-save', () => this.env.bus.trigger('save-partner')); - useListener('click-edit', () => this.editPartner()); useListener('save-changes', this.saveChanges); // We are not using useState here because the object @@ -41,12 +40,8 @@ odoo.define('point_of_sale.PartnerListScreen', function(require) { query: null, selectedPartner: this.props.partner, detailIsShown: false, - // isEditMode: false, editModeProps: { - partner: { - country_id: this.env.pos.company.country_id, - state_id: this.env.pos.company.state_id, - } + partner: null, }, }; this.updatePartnerList = debounce(this.updatePartnerList, 70); @@ -66,6 +61,10 @@ odoo.define('point_of_sale.PartnerListScreen', function(require) { this.props.resolve({ confirmed: true, payload: this.state.selectedPartner }); this.trigger('close-temp-screen'); } + activateEditMode() { + this.state.detailIsShown = true; + this.render(true); + } // Getters get currentOrder() { @@ -79,23 +78,27 @@ odoo.define('point_of_sale.PartnerListScreen', function(require) { } else { res = this.env.pos.db.get_partners_sorted(1000); } - return res.sort(function (a, b) { return (a.name || '').localeCompare(b.name || '') }); - } - get isNextButtonVisible() { - return this.state.selectedPartner ? true : false; - } - /** - * Returns the text and command of the next button. - * The command field is used by the clickNext call. - */ - get nextButton() { - if (!this.props.partner) { - return { command: 'set', text: this.env._t('Set Customer') }; - } else if (this.props.partner && this.props.partner === this.state.selectedPartner) { - return { command: 'deselect', text: this.env._t('Deselect Customer') }; - } else { - return { command: 'set', text: this.env._t('Change Customer') }; + res.sort(function (a, b) { return (a.name || '').localeCompare(b.name || '') }); + // the selected partner (if any) is displayed at the top of the list + if (this.state.selectedPartner) { + let indexOfSelectedPartner = res.findIndex( partner => + partner.id === this.state.selectedPartner.id + ); + if (indexOfSelectedPartner !== -1) { + res.splice(indexOfSelectedPartner, 1); + res.unshift(this.state.selectedPartner); + } } + return res + } + get isEveryPartnerLoadedAfterStartOfSession() { + return !this.env.pos.config.limited_partners_loading || this.env.pos.config.partner_load_background; + } + get isBalanceDisplayed() { + return this.env.pos.config.module_pos_loyalty; + } + get partnerLink() { + return `/web#model=res.partner&id=${this.state.editModeProps.partner.id}`; } // Methods @@ -104,43 +107,37 @@ odoo.define('point_of_sale.PartnerListScreen', function(require) { // order to lower its trigger rate. async updatePartnerList(event) { this.state.query = event.target.value; - const partners = this.partners; - if (event.code === 'Enter' && partners.length === 1) { - this.state.selectedPartner = partners[0]; - this.clickNext(); + if (event.code === 'Enter') { + if (!this.isEveryPartnerLoadedAfterStartOfSession) { + await this.searchPartner(); + } + const partners = this.partners; + if (partners.length === 1) { + this.clickPartner(partners[0]); + } } else { this.render(true); } } clickPartner(partner) { - if (this.state.selectedPartner === partner) { + if (this.state.selectedPartner && this.state.selectedPartner.id === partner.id) { this.state.selectedPartner = null; } else { this.state.selectedPartner = partner; } - this.render(true); - } - editPartner() { - this.state.editModeProps = { - partner: this.state.selectedPartner, - }; - this.state.detailIsShown = true; - this.render(true); - } - clickNext() { - this.state.selectedPartner = this.nextButton.command === 'set' ? this.state.selectedPartner : null; this.confirm(); } - activateEditMode(isNewPartner) { - // this.state.isEditMode = true; - this.state.detailIsShown = true; - this.state.isNewPartner = isNewPartner; - if (!isNewPartner) { - this.state.editModeProps = { - partner: this.state.selectedPartner, - }; + editPartner(partner) { + this.state.editModeProps.partner = partner; + this.activateEditMode(); + } + createPartner() { + // initialize the edit screen with default details about country & state + this.state.editModeProps.partner = { + country_id: this.env.pos.company.country_id, + state_id: this.env.pos.company.state_id, } - this.render(true); + this.activateEditMode(); } async saveChanges(event) { try { @@ -151,8 +148,7 @@ odoo.define('point_of_sale.PartnerListScreen', function(require) { }); await this.env.pos.load_new_partners(); this.state.selectedPartner = this.env.pos.db.get_partner_by_id(partnerId); - this.state.detailIsShown = false; - this.render(true); + this.confirm(); } catch (error) { if (isConnectionError(error)) { await this.showPopup('OfflineErrorPopup', { @@ -178,7 +174,8 @@ odoo.define('point_of_sale.PartnerListScreen', function(require) { async getNewPartners() { let domain = []; if(this.state.query) { - domain = [["name", "ilike", this.state.query + "%"]]; + domain = ['|', ["name", "ilike", this.state.query + "%"], + ["parent_name", "ilike", this.state.query + "%"]]; } const result = await this.env.services.rpc( { diff --git a/addons/point_of_sale/static/src/js/db.js b/addons/point_of_sale/static/src/js/db.js index a14276129b7..b627361ba09 100644 --- a/addons/point_of_sale/static/src/js/db.js +++ b/addons/point_of_sale/static/src/js/db.js @@ -254,6 +254,9 @@ var PosDB = core.Class.extend({ if(partner.vat){ str += '|' + partner.vat; } + if(partner.parent_name){ + str += '|' + partner.parent_name; + } str = '' + partner.id + ':' + str.replace(':', '').replace(/\n/g, ' ') + '\n'; return str; }, diff --git a/addons/point_of_sale/static/src/xml/Screens/PartnerListScreen/PartnerLine.xml b/addons/point_of_sale/static/src/xml/Screens/PartnerListScreen/PartnerLine.xml index 091c534c51f..df2bdb55df8 100644 --- a/addons/point_of_sale/static/src/xml/Screens/PartnerListScreen/PartnerLine.xml +++ b/addons/point_of_sale/static/src/xml/Screens/PartnerListScreen/PartnerLine.xml @@ -5,24 +5,35 @@ - - -
-
+
+ +
+
+ +
+
+ + unselect +
- + - - + +
+ +
+
- - -
+ + + diff --git a/addons/point_of_sale/static/src/xml/Screens/PartnerListScreen/PartnerListScreen.xml b/addons/point_of_sale/static/src/xml/Screens/PartnerListScreen/PartnerListScreen.xml index 16e8dec2b8f..c7c3fc8cb65 100644 --- a/addons/point_of_sale/static/src/xml/Screens/PartnerListScreen/PartnerListScreen.xml +++ b/addons/point_of_sale/static/src/xml/Screens/PartnerListScreen/PartnerListScreen.xml @@ -5,45 +5,33 @@
-
- - Discard -
- - -
- +
+ Save
-
- - + +
+ + Discard +
+
+ +
+
-
- - - - - - Load Customers - -
@@ -63,9 +51,10 @@ Name Address - Phone + Contact ZIP - Email + Balance + @@ -75,6 +64,8 @@ diff --git a/addons/point_of_sale/static/src/xml/Screens/ProductScreen/ProductsWidget.xml b/addons/point_of_sale/static/src/xml/Screens/ProductScreen/ProductsWidget.xml index 741686e2eef..18583d5d1d2 100644 --- a/addons/point_of_sale/static/src/xml/Screens/ProductScreen/ProductsWidget.xml +++ b/addons/point_of_sale/static/src/xml/Screens/ProductScreen/ProductsWidget.xml @@ -11,18 +11,12 @@
-
- -

- No results found for " - - ". -

-
- -

There are no products in this category.

-
-
+

+ No results found for "". Press "Enter" to search the database. +

+

+ There are no products in this category. +

diff --git a/addons/point_of_sale/static/tests/tours/Chrome.tour.js b/addons/point_of_sale/static/tests/tours/Chrome.tour.js index 70563f0c7fb..9e7feba2ea6 100644 --- a/addons/point_of_sale/static/tests/tours/Chrome.tour.js +++ b/addons/point_of_sale/static/tests/tours/Chrome.tour.js @@ -94,7 +94,6 @@ odoo.define('point_of_sale.tour.Chrome', function (require) { ProductScreen.exec.addOrderline('Whiteboard Pen', '5', '6'); ProductScreen.do.clickPartnerButton(); ProductScreen.do.clickCustomer('Nicole Ford'); - ProductScreen.do.clickSetCustomer(); ProductScreen.do.clickPayButton(); PaymentScreen.do.clickPaymentMethod('Bank'); PaymentScreen.do.clickInvoiceButton(); diff --git a/addons/point_of_sale/static/tests/tours/TicketScreen.tour.js b/addons/point_of_sale/static/tests/tours/TicketScreen.tour.js index 6f8303718c7..ac36c73ac83 100644 --- a/addons/point_of_sale/static/tests/tours/TicketScreen.tour.js +++ b/addons/point_of_sale/static/tests/tours/TicketScreen.tour.js @@ -18,14 +18,12 @@ odoo.define('point_of_sale.tour.TicketScreen', function (require) { ProductScreen.exec.addOrderline('Desk Pad', '1', '2'); ProductScreen.do.clickPartnerButton(); ProductScreen.do.clickCustomer('Nicole Ford'); - ProductScreen.do.clickSetCustomer(); Chrome.do.clickTicketButton(); TicketScreen.check.nthRowContains(2, 'Nicole Ford'); TicketScreen.do.clickNewTicket(); ProductScreen.exec.addOrderline('Desk Pad', '1', '3'); ProductScreen.do.clickPartnerButton(); ProductScreen.do.clickCustomer('Brandon Freeman'); - ProductScreen.do.clickSetCustomer(); ProductScreen.do.clickPayButton(); PaymentScreen.check.isShown(); Chrome.do.clickTicketButton(); @@ -79,7 +77,7 @@ odoo.define('point_of_sale.tour.TicketScreen', function (require) { TicketScreen.do.clickControlButton('Invoice'); Chrome.do.confirmPopup(); PartnerListScreen.check.isShown(); - PartnerListScreen.exec.setPartner('Colleen Diaz'); + PartnerListScreen.do.clickPartner('Colleen Diaz'); TicketScreen.check.partnerIs('Colleen Diaz'); // Reprint receipt TicketScreen.do.clickControlButton('Print Receipt'); diff --git a/addons/point_of_sale/static/tests/tours/helpers/PartnerListScreenTourMethods.js b/addons/point_of_sale/static/tests/tours/helpers/PartnerListScreenTourMethods.js index a18cd7f6c60..affaf7a4778 100644 --- a/addons/point_of_sale/static/tests/tours/helpers/PartnerListScreenTourMethods.js +++ b/addons/point_of_sale/static/tests/tours/helpers/PartnerListScreenTourMethods.js @@ -10,24 +10,6 @@ odoo.define('point_of_sale.tour.PartnerListScreenTourMethods', function (require content: `click partner '${name}' from partner list screen`, trigger: `.partnerlist-screen .partner-list-contents .partner-line td:contains("${name}")`, }, - { - content: `check if partner '${name}' is highlighted`, - trigger: `.partnerlist-screen .partner-list-contents .partner-line.highlight td:contains("${name}")`, - run: () => {}, - }, - ]; - } - clickSet() { - return [ - { - content: 'check if set button shown', - trigger: '.partnerlist-screen .button.next.highlight', - run: () => {}, - }, - { - content: 'click set button', - trigger: '.partnerlist-screen .button.next.highlight', - }, ]; } } @@ -44,14 +26,7 @@ odoo.define('point_of_sale.tour.PartnerListScreenTourMethods', function (require } } - class Execute { - setPartner(name) { - const steps = []; - steps.push(...this._do.clickPartner(name)); - steps.push(...this._do.clickSet()); - return steps; - } - } + class Execute {} return createTourMethods('PartnerListScreen', Do, Check, Execute); }); diff --git a/addons/point_of_sale/static/tests/tours/helpers/ProductScreenTourMethods.js b/addons/point_of_sale/static/tests/tours/helpers/ProductScreenTourMethods.js index 35bb6c40e94..931f69e4442 100644 --- a/addons/point_of_sale/static/tests/tours/helpers/ProductScreenTourMethods.js +++ b/addons/point_of_sale/static/tests/tours/helpers/ProductScreenTourMethods.js @@ -108,20 +108,6 @@ odoo.define('point_of_sale.tour.ProductScreenTourMethods', function (require) { content: `select customer '${name}'`, trigger: `.partnerlist-screen .partner-line td:contains("${name}")`, }, - { - content: `partner line '${name}' is highlighted`, - trigger: `.partnerlist-screen .partner-line.highlight td:contains("${name}")`, - run: () => {}, - }, - ]; - } - - clickSetCustomer() { - return [ - { - content: 'click set customer', - trigger: '.partnerlist-screen .button.next.highlight', - }, ]; } diff --git a/addons/point_of_sale/static/tests/tours/point_of_sale.js b/addons/point_of_sale/static/tests/tours/point_of_sale.js index efd5d952976..c6275b1d159 100644 --- a/addons/point_of_sale/static/tests/tours/point_of_sale.js +++ b/addons/point_of_sale/static/tests/tours/point_of_sale.js @@ -100,9 +100,6 @@ odoo.define('point_of_sale.tour.pricelist', function (require) { }, { content: "select Deco Addict", trigger: ".partner-line:contains('Deco Addict')", - }, { - content: "confirm selection", - trigger: ".partnerlist-screen .next", }, { content: "click pricelist button", trigger: ".control-button.o_pricelist_button", @@ -120,9 +117,6 @@ odoo.define('point_of_sale.tour.pricelist', function (require) { content: "select Lumber Inc", trigger: ".partner-line:contains('Lumber Inc')", }, { - content: "confirm selection", - trigger: ".partnerlist-screen .next", - }, { content: "click pricelist button", trigger: ".control-button.o_pricelist_button", }, {