14 changed files with 362 additions and 238 deletions
@ -1,75 +1,57 @@ |
|||
odoo.define('pos_multi_variant.ProductsPopup', function(require) { |
|||
'use strict'; |
|||
/* This JavaScript code defines the ProductsPopup component, which extends |
|||
* the ProductItem class from the point_of_sale module. It represents a popup |
|||
* for selecting product variants. |
|||
*/ |
|||
const Registries = require('point_of_sale.Registries'); |
|||
const { useListener } = require("@web/core/utils/hooks"); |
|||
const ProductItem = require('point_of_sale.ProductItem'); |
|||
/** @odoo-module **/ |
|||
|
|||
class ProductsPopup extends ProductItem { |
|||
setup() { |
|||
super.setup(); |
|||
useListener('click','.confirm', this.click_confirm); |
|||
useListener('click','.product', this.select_variant); |
|||
useListener('click','.cancel', this.click_cancel); |
|||
} |
|||
select_variant(e) { |
|||
var order = this.env.pos.get_order() |
|||
var self = e.composedPath ? e.composedPath()[2] : e.path[2]; |
|||
var action = $(self).find('.action').text(); |
|||
var category = $(self).find('.action').data('category'); |
|||
$('.product-img').find('.variant-selected').each(function () |
|||
{ if($(this).data('category') == category) |
|||
{ $(this).text("").removeClass('variant-selected'); |
|||
$(self).find('.action').text("Selected").addClass('variant-selected'); |
|||
} |
|||
}); |
|||
$(self).find('.action').text("Selected").addClass('variant-selected'); |
|||
var add = $(self).find('.extra-price').text().substr(1).slice(0, -2); |
|||
var type = $(self).find('.variants').text(); |
|||
$(self).find('.variant-selected').attr('data-price', add); |
|||
$(self).find('.variant-selected').attr('data-type', type); |
|||
} |
|||
click_confirm(e){ |
|||
var price = 0.00 |
|||
var order = this.env.pos.get_order() |
|||
var selected_orderline = order.get_selected_orderline() |
|||
var variants = [] |
|||
this.env.pos.selectedOrder.selected_orderline.product_variants=variants |
|||
$('.product-img').find('.variant-selected').each(function () |
|||
{ |
|||
var add = this.previousSibling.innerHTML; |
|||
add = add.slice(3) |
|||
price += parseFloat(add) |
|||
if (order.selected_orderline.product.is_pos_variants){ |
|||
variants.push({ |
|||
'extra_price': add, |
|||
'type': $(this).data('type'), |
|||
}) |
|||
}; |
|||
}) |
|||
selected_orderline.price_manually_set = true; |
|||
selected_orderline.price += price |
|||
this.env.posbus.trigger('close-popup', { |
|||
popupId: this.props.id |
|||
}); |
|||
} |
|||
click_cancel(){ |
|||
this.env.posbus.trigger('close-popup', { |
|||
popupId: this.props.id |
|||
}); |
|||
} |
|||
imageUrl() { |
|||
return `/web/image?model=product.product&field=image_1920&id=${this.props.product_tmpl_id}&unique=1`; |
|||
import Registries from 'point_of_sale.Registries'; |
|||
import { useListener } from "@web/core/utils/hooks"; |
|||
const ProductItem = require('point_of_sale.ProductItem'); |
|||
const { useState } = owl; |
|||
|
|||
|
|||
class ProductsPopup extends ProductItem { |
|||
setup() { |
|||
super.setup(); |
|||
this.state = useState({ |
|||
variant_details: this.props.variant_details, |
|||
selected_variants: {}, |
|||
price_total: {}, |
|||
}) |
|||
} |
|||
|
|||
SelectVariant(product,variant) { |
|||
if (this.state.selected_variants[product.attribute_id[1]] === variant.id){ |
|||
this.state.selected_variants[product.attribute_id[1]] = false |
|||
this.state.price_total[product.attribute_id[1]] = 0.0 |
|||
} |
|||
async _clickProduct(event) { |
|||
else{ |
|||
this.state.selected_variants[product.attribute_id[1]] = variant.id |
|||
this.state.price_total[product.attribute_id[1]] = product.extra_price |
|||
} |
|||
} |
|||
ProductsPopup.template = 'ProductsPopUp'; |
|||
ProductsPopup.defaultProps = {}; |
|||
Registries.Component.add(ProductsPopup); |
|||
return ProductsPopup; |
|||
}); |
|||
|
|||
clickConfirm(e){ |
|||
const total = Object.values(this.state.price_total).reduce((sum, value) => sum + value, 0); |
|||
var order = this.env.pos.get_order() |
|||
var selected_orderline = order.get_selected_orderline() |
|||
selected_orderline.price += total |
|||
this.env.posbus.trigger('close-popup', { |
|||
popupId: this.props.id |
|||
}); |
|||
} |
|||
clickCancel(){ |
|||
this.env.pos.get_order().orderlines.remove(this.env.pos.get_order().selected_orderline) |
|||
this.env.posbus.trigger('close-popup', { |
|||
popupId: this.props.id |
|||
}); |
|||
} |
|||
|
|||
imageUrl() { |
|||
return `/web/image?model=product.product&field=image_1920&id=${this.props.product_tmpl_id}&unique=1`; |
|||
} |
|||
|
|||
getSelected(attr, variant) { |
|||
return this.state.selected_variants[attr] === variant.id |
|||
} |
|||
|
|||
} |
|||
ProductsPopup.template = 'ProductsPopUp'; |
|||
ProductsPopup.defaultProps = {}; |
|||
Registries.Component.add(ProductsPopup); |
|||
|
@ -1,62 +1,59 @@ |
|||
odoo.define('pos_multi_variant.ProductScreen', function(require) { |
|||
'use strict'; |
|||
/* This JavaScript code extends the ProductScreen class from the point_of_sale module. |
|||
* It adds functionality for handling product clicks and displaying the ProductsPopup |
|||
* for selecting variants. |
|||
*/ |
|||
var ProductScreen = require('point_of_sale.ProductScreen'); |
|||
const Registries = require('point_of_sale.Registries'); |
|||
const NumberBuffer = require('point_of_sale.NumberBuffer'); |
|||
var rpc = require('web.rpc'); |
|||
const ProductScreenExtend = (ProductScreen) => |
|||
class extends ProductScreen { |
|||
constructor() { |
|||
super(...arguments); |
|||
/** @odoo-module **/ |
|||
|
|||
import ProductScreen from 'point_of_sale.ProductScreen'; |
|||
import Registries from 'point_of_sale.Registries'; |
|||
import NumberBuffer from 'point_of_sale.NumberBuffer'; |
|||
import rpc from 'web.rpc'; |
|||
|
|||
const ProductScreenExtend = (ProductScreen) => |
|||
class extends ProductScreen { |
|||
setup() { |
|||
super.setup(); |
|||
} |
|||
|
|||
async _clickProduct(event) { |
|||
await super._clickProduct(...arguments) |
|||
if (!this.currentOrder) { |
|||
this.env.pos.add_new_order(); |
|||
} |
|||
async _clickProduct(event) { |
|||
await super._clickProduct(...arguments) |
|||
if (!this.currentOrder) { |
|||
this.env.pos.add_new_order(); |
|||
} |
|||
const product = event.detail; |
|||
var variant_product = '' |
|||
await rpc.query({ |
|||
model: 'variants.tree', |
|||
method: 'search_read', |
|||
fields: ['extra_price','attribute_id','value_ids', 'variants_id'], |
|||
args: [[['variants_id','=',event.detail.product_tmpl_id]]] |
|||
}).then(function (data) { |
|||
variant_product = data |
|||
}); |
|||
var li=[] |
|||
for(var i=0; i<variant_product.length; ++i) { |
|||
variant_product[i].value_ids.forEach(function (field) { |
|||
li.push(field) |
|||
}); |
|||
} |
|||
var variant_details = '' |
|||
await rpc.query({ |
|||
model: 'product.attribute.value', |
|||
method: 'search_read', |
|||
fields: ['name'], |
|||
domain: [['id', 'in', li]], |
|||
}).then(function (result) { |
|||
variant_details = result |
|||
}); |
|||
const options = await this._getAddProductOptions(product); |
|||
// Do not add product if options is undefined.
|
|||
if (!options) return; |
|||
NumberBuffer.reset(); |
|||
if(product.is_pos_variants){ |
|||
this.showPopup('ProductsPopup',{ |
|||
title: product.display_name, |
|||
products: variant_product, |
|||
product_tmpl_id: event.detail.id, |
|||
variant_details: variant_details, |
|||
}); |
|||
} |
|||
} |
|||
}; |
|||
Registries.Component.extend(ProductScreen, ProductScreenExtend); |
|||
return ProductScreen; |
|||
}); |
|||
const product = event.detail; |
|||
var variant_product = '' |
|||
await rpc.query({ |
|||
model: 'variants.tree', |
|||
method: 'search_read', |
|||
fields: ['extra_price','attribute_id','value_ids', 'variants_id'], |
|||
args: [[['variants_id','=',event.detail.product_tmpl_id]]] |
|||
}).then(function (data) { |
|||
variant_product = data |
|||
}); |
|||
var li=[] |
|||
for(var i=0; i<variant_product.length; ++i) { |
|||
variant_product[i].value_ids.forEach(function (field) { |
|||
li.push(field) |
|||
}); |
|||
} |
|||
var variant_details = '' |
|||
await rpc.query({ |
|||
model: 'product.attribute.value', |
|||
method: 'search_read', |
|||
fields: ['name'], |
|||
domain: [['id', 'in', li]], |
|||
}).then(function (result) { |
|||
variant_details = result |
|||
}); |
|||
const options = await this._getAddProductOptions(product); |
|||
// Do not add product if options is undefined.
|
|||
if (!options) return; |
|||
NumberBuffer.reset(); |
|||
if(product.is_pos_variants){ |
|||
this.showPopup('ProductsPopup',{ |
|||
title: product.display_name, |
|||
products: variant_product, |
|||
product_tmpl_id: event.detail.id, |
|||
variant_details: variant_details, |
|||
}); |
|||
} |
|||
} |
|||
}; |
|||
|
|||
Registries.Component.extend(ProductScreen, ProductScreenExtend); |
|||
|
@ -1,21 +1,15 @@ |
|||
odoo.define('pos_multi_variant.model', function(require) { |
|||
'use strict'; |
|||
/* This JavaScript code defines the VariantsPosGlobalState class |
|||
* extending the PosGlobalState class from the point_of_sale.models module. |
|||
* It adds additional functionality to process variants data. |
|||
*/ |
|||
var { |
|||
PosGlobalState |
|||
} = require('point_of_sale.models'); |
|||
const Registries = require('point_of_sale.Registries'); |
|||
const VariantsPosGlobalState = (PosGlobalState) => class VariantsPosGlobalState extends PosGlobalState { |
|||
async _processData(loadedData) { |
|||
/** @odoo-module **/ |
|||
|
|||
await super._processData(...arguments); |
|||
import { PosGlobalState } from 'point_of_sale.models'; |
|||
import Registries from 'point_of_sale.Registries'; |
|||
|
|||
this.variants_tree = loadedData['variants.tree']; |
|||
this.product_attribute_value = loadedData['product.attribute.value']; |
|||
} |
|||
const VariantsPosGlobalState = (PosGlobalState) => class VariantsPosGlobalState extends PosGlobalState { |
|||
async _processData(loadedData) { |
|||
|
|||
await super._processData(...arguments); |
|||
|
|||
this.variants_tree = loadedData['variants.tree']; |
|||
this.product_attribute_value = loadedData['product.attribute.value']; |
|||
} |
|||
Registries.Model.extend(PosGlobalState, VariantsPosGlobalState); |
|||
}); |
|||
} |
|||
Registries.Model.extend(PosGlobalState, VariantsPosGlobalState); |
|||
|
@ -1,34 +1,23 @@ |
|||
odoo.define('pos_order_question.Orderline', function(require) { |
|||
'use strict'; |
|||
/* This JavaScript code defines the PosMultiVariantOrderline class |
|||
* extending the Orderline class from the point_of_sale.models module. |
|||
* It adds additional functionality related to product variants. |
|||
*/ |
|||
var { |
|||
Orderline, |
|||
} = require('point_of_sale.models'); |
|||
var utils = require('web.utils'); |
|||
const Registries = require('point_of_sale.Registries'); |
|||
/** @odoo-module **/ |
|||
|
|||
const PosMultiVariantOrderline = (Orderline) => class PosMultiVariantOrderline extends Orderline { |
|||
import { Orderline } from 'point_of_sale.models'; |
|||
import Registries from 'point_of_sale.Registries'; |
|||
|
|||
constructor(obj, options) { |
|||
super(...arguments); |
|||
this.product_variants = this.product_variants || []; |
|||
} |
|||
export_as_JSON() { |
|||
const json = super.export_as_JSON(...arguments); |
|||
json.product_variants = this.product_variants || []; |
|||
return json; |
|||
} |
|||
export_for_printing() { |
|||
var line = super.export_for_printing(...arguments); |
|||
line.product_variants = this.product_variants; |
|||
return line; |
|||
} |
|||
export const PosMultiVariantOrderline = (Orderline) => class PosMultiVariantOrderline extends Orderline { |
|||
constructor(obj, options) { |
|||
super(...arguments); |
|||
this.product_variants = this.product_variants || []; |
|||
} |
|||
Registries.Model.extend(Orderline, PosMultiVariantOrderline); |
|||
}); |
|||
|
|||
|
|||
export_as_JSON() { |
|||
const json = super.export_as_JSON(...arguments); |
|||
json.product_variants = this.product_variants || []; |
|||
return json; |
|||
} |
|||
export_for_printing() { |
|||
var line = super.export_for_printing(...arguments); |
|||
line.product_variants = this.product_variants; |
|||
return line; |
|||
} |
|||
} |
|||
|
|||
Registries.Model.extend(Orderline, PosMultiVariantOrderline); |
|||
|
@ -1,57 +1,53 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<templates id="template" xml:space="preserve"> |
|||
<!-- This XML snippet defines the ProductsPopUp template --> |
|||
<t t-name="ProductsPopUp" owl="1"> |
|||
<div role="dialog" class="modal-dialog"> |
|||
<div class="popup popup-selection"> |
|||
<header class="title"> |
|||
<t t-esc="props.title || 'Product'" /> |
|||
</header> |
|||
<div class="selection scrollable-y touch-scrollable"> |
|||
<div id="notify"/></div> |
|||
<t t-foreach="props.products" t-as="product" t-key="product.id"> |
|||
<div> |
|||
<h2> |
|||
<t t-esc="product['attribute_id'][1]"/> |
|||
</h2> |
|||
</div> |
|||
<t t-set="attribute" t-value="product['attribute_id'][1]"/> |
|||
<div class="row"> |
|||
<div class="" style="display:flex; flex-wrap: wrap;justify-content:center; padding:0 1.5rem;"> |
|||
<t t-foreach="props.variant_details" t-as="variant" |
|||
t-key="variant.id"> |
|||
<t t-foreach="product['value_ids']" t-as="val" t-key="val"> |
|||
<t t-if="variant['id'] == val"> |
|||
<!-- Display the product variant --> |
|||
<article class='product' style="flex-grow: 1;"> |
|||
<div class='product-img'> |
|||
<img t-att-src="imageUrl()"/> |
|||
<span class='extra-price'>+ |
|||
<t |
|||
t-esc="env.pos.format_currency(product['extra_price'])"/> |
|||
</span> |
|||
<h2 class='action' data-price='' |
|||
data-type='' |
|||
t-att-data-category='attribute'/> |
|||
<span class='variants'> |
|||
<t t-esc="variant['name']"/> |
|||
</span> |
|||
</div> |
|||
</article> |
|||
</t> |
|||
</t> |
|||
</t> |
|||
</div> |
|||
</div> |
|||
<hr/> |
|||
</t> |
|||
<footer class="footer"> |
|||
<div class="button confirm"> Confirm </div> |
|||
<div class="button cancel"> |
|||
Cancel |
|||
</div> |
|||
</footer> |
|||
</div> |
|||
</div> |
|||
</t> |
|||
</templates> |
|||
<t t-name="ProductsPopUp" owl="1"> |
|||
<Draggable> |
|||
<div class="popup popup-selection pos_multi_variant" style=""> |
|||
<header class="title"> |
|||
<t t-esc="props.title || 'Product'"/> |
|||
</header> |
|||
<div class="" style="max-height: 25rem; overflow: auto;"> |
|||
<t t-foreach="props.products" t-as="product" t-key="product.id"> |
|||
<div class="product-category"> |
|||
<h2 class="category-title"> |
|||
<t t-esc="product['attribute_id'][1]"/> |
|||
</h2> |
|||
|
|||
<t t-set="attribute" t-value="product['attribute_id'][1]"/> |
|||
|
|||
<div class="product-grid"> |
|||
<t t-foreach="props.variant_details" t-as="variant" t-key="variant.id"> |
|||
<t t-foreach="product['value_ids']" t-as="val" t-key="val"> |
|||
<t t-if="variant['id'] == val"> |
|||
<article class="product-card" |
|||
t-on-click="()=>this.SelectVariant(product,variant)"> |
|||
<div t-if="getSelected(attribute, variant)" class="ribbon">Selected</div> |
|||
<div class="product-image"> |
|||
<img t-att-src="imageUrl()" alt="Product Variant"/> |
|||
<div class="price-tag"> |
|||
+ <t t-esc="env.pos.format_currency(product['extra_price'])"/> |
|||
</div> |
|||
<div class="variant-name"> |
|||
<t t-esc="variant['name']"/> |
|||
</div> |
|||
</div> |
|||
</article> |
|||
</t> |
|||
</t> |
|||
</t> |
|||
</div> |
|||
</div> |
|||
<hr class="section-divider"/> |
|||
</t> |
|||
</div> |
|||
<footer class="footer"> |
|||
<div class="button confirm" t-on-click="clickConfirm">Confirm</div> |
|||
<div class="button cancel" t-on-click="clickCancel"> |
|||
Cancel |
|||
</div> |
|||
</footer> |
|||
</div> |
|||
</Draggable> |
|||
</t> |
|||
</templates> |
Loading…
Reference in new issue