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 @@